229 lines
5.2 KiB
Go

package actions
import (
"dk/internal/models/fights"
"dk/internal/models/monsters"
"dk/internal/models/spells"
"dk/internal/models/towns"
"dk/internal/models/users"
"math"
"math/rand"
"strconv"
)
func HandleAttack(fight *fights.Fight, user *users.User) {
// Load monster data to get armor
monster, err := monsters.Find(fight.MonsterID)
if err != nil {
fight.AddAction("Monster not found!")
return
}
// Player attack damage calculation
attackPower := float64(user.Attack)
minAttack := attackPower * 0.75
maxAttack := attackPower
tohit := math.Ceil(rand.Float64()*(maxAttack-minAttack)+minAttack) / 3
// Critical hit chance based on strength
criticalRoll := rand.Intn(150) + 1
if float64(criticalRoll) <= math.Sqrt(float64(user.Strength)) {
tohit *= 2 // Critical hit
}
// Monster defense calculation
armor := float64(monster.Armor)
minBlock := armor * 0.75
maxBlock := armor
toblock := math.Ceil(rand.Float64()*(maxBlock-minBlock)+minBlock) / 3
// Calculate final damage
damage := tohit - toblock
if damage < 1 {
damage = 1 // Minimum damage
}
// Apply uber damage bonus
if fight.UberDamage > 0 {
bonus := math.Ceil(damage * float64(fight.UberDamage) / 100)
damage += bonus
}
finalDamage := int(damage)
// Apply damage and add action
fight.DamageMonster(finalDamage)
fight.AddActionAttackHit(finalDamage)
// Check if monster is defeated
if fight.MonsterHP <= 0 {
fight.AddActionMonsterDeath(monster.Name)
fight.WinFight(fight.RewardGold, fight.RewardExp)
HandleFightWin(fight, user)
}
}
func HandleSpell(fight *fights.Fight, user *users.User, spellID int) {
spell, err := spells.Find(spellID)
if err != nil {
fight.AddAction("Spell not found!")
return
}
// Check if user has enough MP
if user.MP < spell.MP {
fight.AddAction("Not enough MP to cast " + spell.Name + "!")
return
}
// Check if user knows this spell
if !user.HasSpell(spellID) {
fight.AddAction("You don't know that spell!")
return
}
// Deduct MP
user.MP -= spell.MP
switch spell.Type {
case spells.TypeHealing:
// Heal user
healAmount := spell.Attribute
user.HP += healAmount
if user.HP > user.MaxHP {
user.HP = user.MaxHP
}
fight.AddAction("You cast " + spell.Name + " and healed " + strconv.Itoa(healAmount) + " HP!")
case spells.TypeHurt:
// Damage monster
damage := spell.Attribute
fight.DamageMonster(damage)
fight.AddAction("You cast " + spell.Name + " and dealt " + strconv.Itoa(damage) + " damage!")
// Check if monster is defeated
if fight.MonsterHP <= 0 {
fight.WinFight(10, 5) // Basic rewards
}
default:
fight.AddAction("You cast " + spell.Name + " but nothing happened!")
}
}
func HandleRun(fight *fights.Fight, user *users.User) {
// 20% chance to successfully run away
if rand.Float32() < 0.2 {
fight.RunAway()
user.FightID = 0
fight.AddAction("You successfully ran away!")
} else {
fight.AddAction("You failed to run away!")
}
}
func HandleMonsterAttack(fight *fights.Fight, user *users.User) {
// Load monster data
monster, err := monsters.Find(fight.MonsterID)
if err != nil {
return
}
// Monster attack damage calculation
attackPower := float64(monster.MaxDmg)
minAttack := attackPower * 0.75
maxAttack := attackPower
tohit := math.Ceil(rand.Float64()*(maxAttack-minAttack)+minAttack) / 3
// User defense calculation
defense := float64(user.Defense)
minBlock := defense * 0.75
maxBlock := defense
toblock := math.Ceil(rand.Float64()*(maxBlock-minBlock)+minBlock) / 3
// Calculate final damage
damage := tohit - toblock
if damage < 1 {
damage = 1 // Minimum damage
}
// Apply uber defense bonus (reduces damage taken)
if fight.UberDefense > 0 {
reduction := math.Ceil(damage * float64(fight.UberDefense) / 100)
damage -= reduction
if damage < 1 {
damage = 1 // Still minimum 1 damage
}
}
finalDamage := int(damage)
// Apply damage to user
user.HP -= finalDamage
if user.HP < 0 {
user.HP = 0
}
// Add monster attack action using memory-optimized format
fight.AddActionMonsterAttack(monster.Name, finalDamage)
// Check if user is defeated
if user.HP <= 0 {
fight.LoseFight()
HandleFightLoss(fight, user)
}
}
func HandleFightWin(fight *fights.Fight, user *users.User) {
// Add rewards to user
user.Exp += fight.RewardExp
user.Gold += fight.RewardGold
// Reset fight state
user.FightID = 0
user.Currently = "Exploring"
fight.Save()
user.Save()
}
func HandleFightLoss(fight *fights.Fight, user *users.User) {
// Find closest town to user's position
closestTown := findClosestTown(user.X, user.Y)
if closestTown != nil {
user.X = closestTown.X
user.Y = closestTown.Y
}
// Apply death penalties
user.HP = user.MaxHP / 4 // 25% of max health
user.Gold = (user.Gold * 3) / 4 // 75% of gold
// Reset fight state
user.FightID = 0
user.Currently = "In Town"
fight.Save()
user.Save()
}
func findClosestTown(x, y int) *towns.Town {
allTowns, err := towns.All()
if err != nil || len(allTowns) == 0 {
return nil
}
var closest *towns.Town
var minDistance float64
for _, town := range allTowns {
distance := town.DistanceFromSquared(x, y)
if closest == nil || distance < minDistance {
closest = town
minDistance = distance
}
}
return closest
}