reimplement fight action flashes

This commit is contained in:
Sky Johnson 2025-08-25 22:12:20 -05:00
parent ef95b70eb9
commit 63dabb9e54
4 changed files with 58 additions and 13 deletions

View File

@ -9,6 +9,7 @@ import (
"dk/internal/models/spells"
"dk/internal/models/towns"
"dk/internal/models/users"
"fmt"
"math"
"math/rand"
)
@ -17,6 +18,7 @@ type FightResult struct {
FightUpdates map[string]any
UserUpdates map[string]any
LogAction func() error
ActionText string // Add this field
Ended bool
Victory bool
Won bool
@ -55,7 +57,8 @@ func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
monster, err := monsters.Find(fight.MonsterID)
if err != nil {
return &FightResult{
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Monster not found!") },
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Monster not found!") },
ActionText: "Monster not found!",
}
}
@ -66,9 +69,13 @@ func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
rawAttack := math.Ceil(rand.Float64()*(maxAttack-minAttack) + minAttack)
tohit := rawAttack / (1.2 + math.Sqrt(attackPower)*0.05)
damageMultiplier := 1 + math.Pow(float64(user.Attack)/400, 0.6)
tohit *= damageMultiplier
// Critical hit
criticalRoll := rand.Intn(150) + 1
if float64(criticalRoll) <= math.Sqrt(float64(user.Strength)) {
critChance := 85 * math.Pow(float64(user.Strength)/300, 0.6)
criticalRoll := rand.Float64() * 100
if criticalRoll <= critChance {
tohit *= 2
}
@ -92,14 +99,18 @@ func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
finalDamage := int(damage)
newMonsterHP := max(fight.MonsterHP-finalDamage, 0)
actionText := fmt.Sprintf("You attacked for %d damage!", finalDamage)
result := &FightResult{
FightUpdates: map[string]any{"monster_hp": newMonsterHP},
LogAction: func() error { return fightlogs.AddAttackHit(fight.ID, finalDamage) },
ActionText: actionText,
}
// Check if monster defeated
if newMonsterHP <= 0 {
result.EndFightWithVictory(monster, user)
result.ActionText = actionText + " " + fmt.Sprintf("%s has been defeated!", monster.Name)
result.LogAction = func() error {
if err := fightlogs.AddAttackHit(fight.ID, finalDamage); err != nil {
return err
@ -115,19 +126,24 @@ func HandleSpell(fight *fights.Fight, user *users.User, spellID int) *FightResul
spell, err := spells.Find(spellID)
if err != nil {
return &FightResult{
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Spell not found!") },
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Spell not found!") },
ActionText: "Spell not found!",
}
}
if user.MP < spell.MP {
actionText := "Not enough MP to cast " + spell.Name + "!"
return &FightResult{
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Not enough MP to cast "+spell.Name+"!") },
LogAction: func() error { return fightlogs.AddAction(fight.ID, actionText) },
ActionText: actionText,
}
}
if !user.HasSpell(spellID) {
actionText := "You don't know that spell!"
return &FightResult{
LogAction: func() error { return fightlogs.AddAction(fight.ID, "You don't know that spell!") },
LogAction: func() error { return fightlogs.AddAction(fight.ID, actionText) },
ActionText: actionText,
}
}
@ -139,22 +155,26 @@ func HandleSpell(fight *fights.Fight, user *users.User, spellID int) *FightResul
case spells.TypeHealing:
newHP := min(user.HP+spell.Attribute, user.MaxHP)
result.UserUpdates["hp"] = newHP
result.ActionText = fmt.Sprintf("You cast %s and healed %d HP!", spell.Name, spell.Attribute)
result.LogAction = func() error { return fightlogs.AddSpellHeal(fight.ID, spell.Name, spell.Attribute) }
case spells.TypeHurt:
newMonsterHP := max(fight.MonsterHP-spell.Attribute, 0)
result.FightUpdates = map[string]any{"monster_hp": newMonsterHP}
result.ActionText = fmt.Sprintf("You cast %s and dealt %d damage!", spell.Name, spell.Attribute)
result.LogAction = func() error { return fightlogs.AddSpellHurt(fight.ID, spell.Name, spell.Attribute) }
if newMonsterHP <= 0 {
monster, err := monsters.Find(fight.MonsterID)
if err == nil {
result.EndFightWithVictory(monster, user)
result.ActionText += " " + fmt.Sprintf("%s has been defeated!", monster.Name)
}
}
default:
result.LogAction = func() error { return fightlogs.AddAction(fight.ID, "You cast "+spell.Name+" but nothing happened!") }
result.ActionText = "You cast " + spell.Name + " but nothing happened!"
result.LogAction = func() error { return fightlogs.AddAction(fight.ID, result.ActionText) }
}
return result
@ -169,9 +189,11 @@ func HandleRun(fight *fights.Fight, user *users.User) *FightResult {
"fight_id": 0,
"currently": "Exploring",
}
result.ActionText = "You successfully ran away!"
result.LogAction = func() error { return fightlogs.AddRunSuccess(fight.ID) }
result.Ended = true
} else {
result.ActionText = "You failed to run away!"
result.LogAction = func() error { return fightlogs.AddRunFail(fight.ID) }
}
@ -213,6 +235,7 @@ func HandleMonsterAttack(fight *fights.Fight, user *users.User) *FightResult {
result := &FightResult{
UserUpdates: map[string]any{"hp": newHP},
ActionText: fmt.Sprintf("%s attacks for %d damage!", monster.Name, finalDamage),
LogAction: func() error { return fightlogs.AddMonsterAttack(fight.ID, monster.Name, finalDamage) },
}

View File

@ -65,8 +65,10 @@ func showFight(ctx sushi.Ctx) {
if fight.Turn == 0 {
err := database.Transaction(func() error {
return database.Update("fights", map[string]any{
"first_strike": rand.Float32() < 0.5,
"turn": 1,
"first_strike": rand.Float32() < 0.5,
"turn": 1,
"monster_hp": monster.MaxHP,
"monster_max_hp": monster.MaxHP,
}, "id", fight.ID)
})
if err != nil {
@ -75,6 +77,8 @@ func showFight(ctx sushi.Ctx) {
}
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)
@ -133,14 +137,16 @@ func handleFightAction(ctx sushi.Ctx) {
result = actions.HandleSpell(fight, user, spellID)
} else {
result = &actions.FightResult{
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid spell!") },
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!") },
LogAction: func() error { return fightlogs.AddAction(fight.ID, "Invalid action!") },
ActionText: "Invalid action!",
}
}
@ -151,6 +157,11 @@ func handleFightAction(ctx sushi.Ctx) {
return
}
// Flash player action
if result.ActionText != "" {
sess.SetFlash("action", result.ActionText)
}
// Handle fight end states
if result.Ended {
if result.Won {
@ -169,6 +180,13 @@ func handleFightAction(ctx sushi.Ctx) {
// 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
@ -178,6 +196,11 @@ func handleFightAction(ctx sushi.Ctx) {
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 {

View File

@ -95,7 +95,7 @@ func Explore(ctx sushi.Ctx) {
func Teleport(ctx sushi.Ctx) {
sess := ctx.GetCurrentSession()
id := ctx.Param("id").Int()
id := ctx.Param("to").Int()
town, err := towns.Find(id)
if err != nil {

View File

@ -28,7 +28,6 @@
<section>
<h5>Town Maps</h5>
{if #_towns > 0}
<i>Teleport to:</i>
{for t in _towns}
{if town != nil and t.Name == town.Name}
<span>{t.Name} <i>(here)</i></span>