230 lines
5.5 KiB
Go
230 lines
5.5 KiB
Go
package routes
|
|
|
|
import (
|
|
"dk/internal/actions"
|
|
"dk/internal/components"
|
|
"dk/internal/database"
|
|
"dk/internal/helpers"
|
|
"dk/internal/models/fightlogs"
|
|
"dk/internal/models/fights"
|
|
"dk/internal/models/monsters"
|
|
"dk/internal/models/spells"
|
|
"dk/internal/models/users"
|
|
"fmt"
|
|
"math/rand"
|
|
"strconv"
|
|
|
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
|
)
|
|
|
|
func RegisterFightRoutes(app *sushi.App) {
|
|
group := app.Group("/fight")
|
|
group.Use(auth.RequireAuth("/login"))
|
|
group.Use(requireFighting())
|
|
|
|
group.Get("/", showFight)
|
|
group.Post("/", handleFightAction)
|
|
}
|
|
|
|
func requireFighting() sushi.Middleware {
|
|
return func(ctx sushi.Ctx, next func()) {
|
|
user := ctx.GetCurrentUser()
|
|
if user == nil {
|
|
ctx.SendError(401, "Not authenticated")
|
|
return
|
|
}
|
|
|
|
userModel := user.(*users.User)
|
|
if !userModel.IsFighting() {
|
|
ctx.Redirect("/")
|
|
return
|
|
}
|
|
|
|
next()
|
|
}
|
|
}
|
|
|
|
func showFight(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
|
|
fight, err := fights.Find(user.FightID)
|
|
if err != nil {
|
|
ctx.SendError(404, "Fight not found")
|
|
return
|
|
}
|
|
|
|
monster, err := monsters.Find(fight.MonsterID)
|
|
if err != nil {
|
|
ctx.SendError(404, "Monster not found for fight")
|
|
return
|
|
}
|
|
|
|
// Initialize fight on first view
|
|
if fight.Turn == 0 {
|
|
err := database.Transaction(func() error {
|
|
return database.Update("fights", map[string]any{
|
|
"first_strike": rand.Float32() < 0.5,
|
|
"turn": 1,
|
|
"monster_hp": monster.MaxHP,
|
|
"monster_max_hp": monster.MaxHP,
|
|
}, "id", fight.ID)
|
|
})
|
|
if err != nil {
|
|
ctx.SendError(500, "Failed to initialize fight")
|
|
return
|
|
}
|
|
fight.FirstStrike = rand.Float32() < 0.5
|
|
fight.Turn = 1
|
|
fight.MonsterHP = monster.MaxHP
|
|
fight.MonsterMaxHP = monster.MaxHP
|
|
}
|
|
|
|
monHpPct := helpers.ClampPct(float64(fight.MonsterHP), float64(fight.MonsterMaxHP), 0, 100)
|
|
monHpColor := ""
|
|
if monHpPct < 35 {
|
|
monHpColor = "danger"
|
|
} else if monHpPct < 75 {
|
|
monHpColor = "warning"
|
|
}
|
|
|
|
spellMap := helpers.NewOrderedMap[int, *spells.Spell]()
|
|
if user.Spells != "" {
|
|
for _, id := range user.GetSpellIDs() {
|
|
if spell, err := spells.Find(id); err == nil {
|
|
spellMap.Set(id, spell)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get recent fight actions
|
|
lastAction, _ := fightlogs.GetLastAction(fight.ID)
|
|
|
|
components.RenderPage(ctx, "Fighting", "fight/fight.html", map[string]any{
|
|
"fight": fight,
|
|
"user": user,
|
|
"monster": monster,
|
|
"mon_hppct": monHpPct,
|
|
"mon_hpcol": monHpColor,
|
|
"spells": spellMap.ToSlice(),
|
|
"action": sess.GetFlashMessage("action"),
|
|
"mon_action": sess.GetFlashMessage("mon_action"),
|
|
"last_action": lastAction,
|
|
})
|
|
}
|
|
|
|
func handleFightAction(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
|
|
fight, err := fights.Find(user.FightID)
|
|
if err != nil {
|
|
ctx.SetContentType("text/plain")
|
|
ctx.SetBodyString("Fight not found")
|
|
return
|
|
}
|
|
|
|
action := string(ctx.FormValue("action"))
|
|
var result *actions.FightResult
|
|
|
|
switch action {
|
|
case "attack":
|
|
result = actions.HandleAttack(fight, user)
|
|
case "spell":
|
|
spellIDStr := string(ctx.FormValue("spell_id"))
|
|
if spellID, err := strconv.Atoi(spellIDStr); err == nil {
|
|
result = actions.HandleSpell(fight, user, spellID)
|
|
} else {
|
|
result = &actions.FightResult{
|
|
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid spell!") },
|
|
ActionText: "Invalid spell!",
|
|
}
|
|
}
|
|
case "run":
|
|
result = actions.HandleRun(fight, user)
|
|
default:
|
|
result = &actions.FightResult{
|
|
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid action!") },
|
|
ActionText: "Invalid action!",
|
|
}
|
|
}
|
|
|
|
// Execute the action
|
|
err = actions.ExecuteFightAction(fight.ID, result)
|
|
if err != nil {
|
|
ctx.SendError(500, "Failed to execute fight action")
|
|
return
|
|
}
|
|
|
|
// Flash player action
|
|
if result.ActionText != "" {
|
|
sess.SetFlash("action", result.ActionText)
|
|
}
|
|
|
|
// Handle fight end states
|
|
if result.Ended {
|
|
if result.Won {
|
|
sess.SetFlash("success", fmt.Sprintf("Victory! You gained %d gold and %d experience!", result.RewardGold, result.RewardExp))
|
|
ctx.Redirect("/explore", 302)
|
|
} else if result.Victory {
|
|
sess.SetFlash("error", "You have been defeated! You lost some gold and were sent to the nearest town.")
|
|
ctx.Redirect("/town", 302)
|
|
} else {
|
|
// Ran away
|
|
sess.SetFlash("success", "You successfully escaped!")
|
|
ctx.Redirect("/explore", 302)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Monster attacks back if fight continues
|
|
if fight.IsActive() && user.HP > 0 {
|
|
// Refresh user data after player action
|
|
user, err = users.Find(user.ID)
|
|
if err != nil {
|
|
ctx.SendError(500, "Failed to refresh user data")
|
|
return
|
|
}
|
|
|
|
monsterResult := actions.HandleMonsterAttack(fight, user)
|
|
|
|
// Execute monster action
|
|
err = actions.ExecuteFightAction(fight.ID, monsterResult)
|
|
if err != nil {
|
|
ctx.SendError(500, "Failed to execute monster action")
|
|
return
|
|
}
|
|
|
|
// Flash monster action
|
|
if monsterResult.ActionText != "" {
|
|
sess.SetFlash("mon_action", monsterResult.ActionText)
|
|
}
|
|
|
|
// Check if monster action ended fight
|
|
if monsterResult.Ended {
|
|
if monsterResult.Won {
|
|
sess.SetFlash("success", "Victory!")
|
|
ctx.Redirect("/explore", 302)
|
|
} else {
|
|
sess.SetFlash("error", "You have been defeated!")
|
|
ctx.Redirect("/town", 302)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
// Increment turn
|
|
err = database.Transaction(func() error {
|
|
return database.Update("fights", map[string]any{
|
|
"turn": fight.Turn + 1,
|
|
}, "id", fight.ID)
|
|
})
|
|
if err != nil {
|
|
ctx.SendError(500, "Failed to increment turn")
|
|
return
|
|
}
|
|
|
|
ctx.Redirect("/fight", 302)
|
|
}
|