aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2025-08-06 15:20:15 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2025-08-06 15:20:43 +0200
commita0345c11f8035b300330e6b7c59a654c1e1e231d (patch)
tree9dab905deb708b3c7a2675ee7a95d6d43087b286
parent0cff8c7704187224053a068a1b3a6c166ddfdd6d (diff)
downloadmuhqs-game-a0345c11f8035b300330e6b7c59a654c1e1e231d.tar.gz
muhqs-game-a0345c11f8035b300330e6b7c59a654c1e1e231d.zip
add more challenges
-rw-r--r--go/client/challenges.go76
-rw-r--r--go/client/startMenu.go17
-rw-r--r--go/game/artifact.go1
-rw-r--r--go/game/challenge.go78
-rw-r--r--go/game/winCondition.go11
5 files changed, 155 insertions, 28 deletions
diff --git a/go/client/challenges.go b/go/client/challenges.go
new file mode 100644
index 00000000..7a9e1ace
--- /dev/null
+++ b/go/client/challenges.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "muhq.space/muhqs-game/go/activities"
+ "muhq.space/muhqs-game/go/game"
+ "muhq.space/muhqs-game/go/ui"
+)
+
+type challenges struct {
+ ui.Collection
+ app *app
+
+ playerName string
+}
+
+func NewChallengesMenu(app *app, playerName string) *challenges {
+ m := &challenges{app: app, playerName: playerName}
+ m.Width = app.windowWidth
+ m.Height = app.windowHeight
+
+ return m
+}
+
+var availChallenges map[string]func(string) *game.LocalState
+
+func init() {
+ availChallenges = make(map[string]func(string) *game.LocalState)
+ availChallenges["Missionary"] = game.NewMissionaryChallenge
+ availChallenges["Cav. Archer"] = game.NewCavArcherChallenge
+ availChallenges["Random"] = game.NewRandomChallenge
+}
+
+func (m *challenges) build() {
+ i := 0
+ for ch, f := range availChallenges {
+
+ m.AddWidget(ui.NewSimpleButton(
+ (m.Width-START_BUTTON_WIDTH)/2,
+ i*START_BUTTON_HEIGHT+50+i*25,
+ START_BUTTON_WIDTH,
+ START_BUTTON_HEIGHT,
+ ch,
+ func(*ui.SimpleButton) {
+ s := f(m.playerName)
+ m.startChallenge(s)
+ }))
+ i = i + 1
+ }
+}
+
+func (m *challenges) Update() error {
+ if m.Widgets() == nil {
+ m.build()
+ }
+
+ if err := ui.Update(); err != nil {
+ return err
+ }
+
+ return m.Collection.Update()
+}
+
+func (m *challenges) Layout(width, height int) (int, int) {
+ return m.Width, m.Height
+}
+
+func (m *challenges) startChallenge(state game.State) {
+ g := newGame(m.app, state)
+
+ g.initMapUi()
+ p := state.PlayerByName(m.playerName)
+ g.activePlayerId = p.Id
+ g.initPlayerUi(p)
+
+ activities.PushActivity(g.Start())
+}
diff --git a/go/client/startMenu.go b/go/client/startMenu.go
index 2c3225b9..aef43ab0 100644
--- a/go/client/startMenu.go
+++ b/go/client/startMenu.go
@@ -137,11 +137,9 @@ func (m *startMenu) build() {
deckInput.Y+deckInput.Height+4*START_BUTTON_HEIGHT,
START_BUTTON_WIDTH,
START_BUTTON_HEIGHT,
- "Challenge",
+ "Challenges",
func(*ui.SimpleButton) {
- m.playerName = playerInput.Text()
- s := game.NewRandomChallenge(m.playerName)
- m.startChallenge(s)
+ activities.PushActivity(NewChallengesMenu(m.app, playerInput.Text()))
}))
}
@@ -189,14 +187,3 @@ func (m *startMenu) startGame() {
activities.PushActivity(g.Start())
}
-
-func (m *startMenu) startChallenge(state game.State) {
- g := newGame(m.app, state)
-
- g.initMapUi()
- p := state.PlayerByName(m.playerName)
- g.activePlayerId = p.Id
- g.initPlayerUi(p)
-
- activities.PushActivity(g.Start())
-}
diff --git a/go/game/artifact.go b/go/game/artifact.go
index 35c05893..d3b9f0b2 100644
--- a/go/game/artifact.go
+++ b/go/game/artifact.go
@@ -1,6 +1,5 @@
package game
-
type Artifact struct {
permanentBase
Solid int
diff --git a/go/game/challenge.go b/go/game/challenge.go
index b27ef162..6e314b26 100644
--- a/go/game/challenge.go
+++ b/go/game/challenge.go
@@ -5,25 +5,34 @@ import (
"math/rand"
)
-func NewRandomChallenge(name string) *LocalState {
+func prepState(playerName, mapName string) *LocalState {
s := NewLocalState()
- maps := []string{"2P-ring-street", "2P-river-king"}
- if m, err := GetMap(maps[rand.Intn(len(maps))]); err != nil {
+ if m, err := GetMap(mapName); err != nil {
log.Panic(err)
} else {
s.SetMap(m)
}
// Prepare the players
- s.AddNewPlayer(name, NewDeck())
+ s.AddNewPlayer(playerName, NewDeck())
// Add Opponent
ai := s.AddNewPlayer("opponent", NewDeck())
ai.Ctrl = NewSimpleAiControl(ai)
- switch rand.Intn(2) {
+ return s
+}
+
+func NewRandomChallenge(name string) *LocalState {
+ maps := []string{"2P-ring-street", "2P-river-king"}
+ m := maps[rand.Intn(len(maps))]
+ s := prepState(name, m)
+
+ switch rand.Intn(3) {
// Cavalry Archer Challenge
case 0:
return prepCavArcherChallenge(s)
+ case 1:
+ return prepMissionaryChallenge(s)
// Random Base Challenge
default:
return prepBaseChallenge(s)
@@ -85,20 +94,65 @@ func prepCavArcherChallenge(s *LocalState) *LocalState {
s.addNewUnit(u, tile.Position, ai)
}
- s._map.WinCondition = func(*LocalState) []*Player {
- winners := []*Player{}
- for _, p := range s.players {
- if len(s.EnemyUnits(p)) == 0 {
- winners = append(winners, p)
- }
+ s._map.WinCondition = DeathMatch
+ }
+
+ return s
+}
+
+func NewCavArcherChallenge(playerName string) *LocalState {
+ maps := []string{"2P-ring-street", "2P-river-king"}
+ m := maps[rand.Intn(len(maps))]
+ return prepCavArcherChallenge(prepState(playerName, m))
+}
+
+func prepMissionaryChallenge(s *LocalState) *LocalState {
+ // Override old Prepare to prevent Kings from beeing spawned
+ s._map.Prepare = func(*LocalState) {
+ u := NewCard("base/missionary")
+ tiles := s._map.AvailableTilesFor(u)
+ tile := tiles[rand.Intn(len(tiles))]
+ p := s.PlayerById(1)
+ p.Deck = NewDeck()
+ s.addNewUnit(u, tile.Position, p)
+
+ // Prepare the opponent's units
+ nUnits := rand.Intn(2) + 2
+
+ units := make([]*Card, 0, nUnits)
+ ai := s.PlayerById(2)
+ // Only spawn aggressive Units
+ candidates := NewDeckFromCardPaths(Sets.Base.CardPaths()).FilterCards(func(c *Card) bool {
+ if c.Type != CardTypes.Unit {
+ return false
}
- return winners
+
+ _, found := c.Values["attack"]
+ return found
+ })
+
+ for range nUnits {
+ units = append(units, NewCard(candidates[rand.Intn(len(candidates))].Path()))
}
+
+ for _, u := range units {
+ tiles := s._map.AvailableTilesFor(u)
+ tile := tiles[rand.Intn(len(tiles))]
+ s.addNewUnit(u, tile.Position, ai)
+ }
+
+ s._map.WinCondition = DeathMatch
}
return s
}
+func NewMissionaryChallenge(playerName string) *LocalState {
+ maps := []string{"2P-ring-street", "2P-river-king"}
+ m := maps[rand.Intn(len(maps))]
+ return prepMissionaryChallenge(prepState(playerName, m))
+}
+
func prepBaseChallenge(s *LocalState) *LocalState {
p := s.PlayerById(1)
nCards := rand.Intn(6)
diff --git a/go/game/winCondition.go b/go/game/winCondition.go
index d08f4135..ded9b0ca 100644
--- a/go/game/winCondition.go
+++ b/go/game/winCondition.go
@@ -43,3 +43,14 @@ func KingGame(s *LocalState) []*Player {
}
return winners
}
+
+// DeathMatch returns the players without enemy &nits.
+func DeathMatch(s *LocalState) []*Player {
+ winners := []*Player{}
+ for _, p := range s.players {
+ if len(s.EnemyUnits(p)) == 0 {
+ winners = append(winners, p)
+ }
+ }
+ return winners
+}