fix lots of little bugs and get last_online working

This commit is contained in:
Sky Johnson 2025-08-22 09:09:15 -05:00
parent ee19412965
commit cfc1256d02
11 changed files with 95 additions and 89 deletions

Binary file not shown.

View File

@ -340,7 +340,7 @@ CREATE TABLE towns (
`shop_list` TEXT NOT NULL
);
INSERT INTO towns VALUES
(1, 'Midworld', 0, 0, 5, 0, 0, '1,2,3,17,18,19,28,29'),
(1, 'Midworld', 0, 0, 1, 0, 0, '1,2,3,17,18,19,28,29'),
(2, 'Roma', 30, 30, 10, 25, 5, '2,3,4,18,19,29'),
(3, 'Bris', 70, -70, 25, 50, 15, '2,3,4,5,18,19,20,29.30'),
(4, 'Kalle', -100, 100, 40, 100, 30, '5,6,8,10,12,21,22,23,29,30'),

2
go.mod
View File

@ -3,7 +3,7 @@ module dk
go 1.25.0
require (
git.sharkk.net/Sharkk/Sushi v1.1.1
git.sharkk.net/Sharkk/Sushi v1.2.0
github.com/valyala/fasthttp v1.65.0
zombiezen.com/go/sqlite v1.4.2
)

4
go.sum
View File

@ -1,5 +1,5 @@
git.sharkk.net/Sharkk/Sushi v1.1.1 h1:ynU16l6vAhY/JUwHlI4zMQiPuL9lcs88W/mAGZsL4Rw=
git.sharkk.net/Sharkk/Sushi v1.1.1/go.mod h1:S84ACGkuZ+BKzBO4lb5WQnm5aw9+l7VSO2T1bjzxL3o=
git.sharkk.net/Sharkk/Sushi v1.2.0 h1:RwOCZmgaOqtkmuK2Z7/esdLbhSXJZphsOsWEHni4Sss=
git.sharkk.net/Sharkk/Sushi v1.2.0/go.mod h1:S84ACGkuZ+BKzBO4lb5WQnm5aw9+l7VSO2T1bjzxL3o=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=

View File

@ -20,6 +20,38 @@ type FightResult struct {
Ended bool
Victory bool
Won bool
RewardGold int
RewardExp int
}
func (r *FightResult) EndFightWithVictory(monster *monsters.Monster, user *users.User) {
rewardGold, rewardExp := calculateRewards(monster, user)
r.FightUpdates["victory"] = true
r.FightUpdates["won"] = true
r.FightUpdates["reward_gold"] = rewardGold
r.FightUpdates["reward_exp"] = rewardExp
r.UserUpdates = map[string]any{
"fight_id": 0,
"currently": "Exploring",
"gold": user.Gold + rewardGold,
"exp": user.Exp + rewardExp,
}
// Handle level up
newLevel, newStats := calculateLevelUp(user.Level, user.Exp+rewardExp, user.Strength, user.Dexterity)
if newLevel > user.Level {
r.UserUpdates["level"] = newLevel
r.UserUpdates["strength"] = newStats.Strength
r.UserUpdates["dexterity"] = newStats.Dexterity
}
r.Ended = true
r.Victory = true
r.Won = true
r.RewardGold = rewardGold
r.RewardExp = rewardExp
}
func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
@ -61,10 +93,7 @@ func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
}
finalDamage := int(damage)
newMonsterHP := fight.MonsterHP - finalDamage
if newMonsterHP < 0 {
newMonsterHP = 0
}
newMonsterHP := max(fight.MonsterHP-finalDamage, 0)
result := &FightResult{
FightUpdates: map[string]any{"monster_hp": newMonsterHP},
@ -73,37 +102,13 @@ func HandleAttack(fight *fights.Fight, user *users.User) *FightResult {
// Check if monster defeated
if newMonsterHP <= 0 {
rewardGold, rewardExp := calculateRewards(monster, user)
result.FightUpdates["victory"] = true
result.FightUpdates["won"] = true
result.FightUpdates["reward_gold"] = rewardGold
result.FightUpdates["reward_exp"] = rewardExp
result.UserUpdates = map[string]any{
"fight_id": 0,
"currently": "Exploring",
"gold": user.Gold + rewardGold,
"exp": user.Exp + rewardExp,
}
// Handle level up
newLevel, newStats := calculateLevelUp(user.Level, user.Exp+rewardExp, user.Strength, user.Dexterity)
if newLevel > user.Level {
result.UserUpdates["level"] = newLevel
result.UserUpdates["strength"] = newStats.Strength
result.UserUpdates["dexterity"] = newStats.Dexterity
}
result.EndFightWithVictory(monster, user)
result.LogAction = func() error {
if err := fightlogs.AddAttackHit(fight.ID, finalDamage); err != nil {
return err
}
return fightlogs.AddMonsterDeath(fight.ID, monster.Name)
}
result.Ended = true
result.Victory = true
result.Won = true
}
return result
@ -135,31 +140,20 @@ func HandleSpell(fight *fights.Fight, user *users.User, spellID int) *FightResul
switch spell.Type {
case spells.TypeHealing:
newHP := user.HP + spell.Attribute
if newHP > user.MaxHP {
newHP = user.MaxHP
}
newHP := min(user.HP+spell.Attribute, user.MaxHP)
result.UserUpdates["hp"] = newHP
result.LogAction = func() error { return fightlogs.AddSpellHeal(fight.ID, spell.Name, spell.Attribute) }
case spells.TypeHurt:
newMonsterHP := fight.MonsterHP - spell.Attribute
if newMonsterHP < 0 {
newMonsterHP = 0
}
newMonsterHP := max(fight.MonsterHP-spell.Attribute, 0)
result.FightUpdates = map[string]any{"monster_hp": newMonsterHP}
result.LogAction = func() error { return fightlogs.AddSpellHurt(fight.ID, spell.Name, spell.Attribute) }
if newMonsterHP <= 0 {
result.FightUpdates["victory"] = true
result.FightUpdates["won"] = true
result.FightUpdates["reward_gold"] = 10
result.FightUpdates["reward_exp"] = 5
result.UserUpdates["fight_id"] = 0
result.UserUpdates["currently"] = "Exploring"
result.Ended = true
result.Victory = true
result.Won = true
monster, err := monsters.Find(fight.MonsterID)
if err == nil {
result.EndFightWithVictory(monster, user)
}
}
default:
@ -218,10 +212,7 @@ func HandleMonsterAttack(fight *fights.Fight, user *users.User) *FightResult {
}
finalDamage := int(damage)
newHP := user.HP - finalDamage
if newHP < 0 {
newHP = 0
}
newHP := max(user.HP-finalDamage, 0)
result := &FightResult{
UserUpdates: map[string]any{"hp": newHP},
@ -301,14 +292,22 @@ func findClosestTown(x, y int) *towns.Town {
}
func calculateRewards(monster *monsters.Monster, user *users.User) (int, int) {
minExp := (monster.MaxExp * 5) / 6
// More generous reward range: 90-100% of max instead of 83-100%
minExp := (monster.MaxExp * 9) / 10
maxExp := monster.MaxExp
exp := rand.Intn(maxExp-minExp+1) + minExp
if exp < 1 {
exp = 1 // Guaranteed minimum
}
minGold := (monster.MaxGold * 5) / 6
minGold := (monster.MaxGold * 9) / 10
maxGold := monster.MaxGold
gold := rand.Intn(maxGold-minGold+1) + minGold
if gold < 1 {
gold = 1 // Guaranteed minimum
}
// Apply bonuses
expBonus := (user.ExpBonus * exp) / 100
exp += expBonus

View File

@ -44,7 +44,7 @@ func RightAside(ctx sushi.Ctx) map[string]any {
return data
}
user := ctx.UserValue("user").(*users.User)
user := ctx.GetCurrentUser().(*users.User)
data["_expprog"] = fmt.Sprintf("%.1f", user.ExpProgress())

View File

@ -95,46 +95,35 @@ func showRegister(ctx sushi.Ctx) {
// processRegister handles registration form submission
func processRegister(ctx sushi.Ctx) {
fmt.Println("DEBUG: Starting registration process")
username := strings.TrimSpace(ctx.Form("username").String())
email := strings.TrimSpace(ctx.Form("email").String())
userPassword := ctx.Form("password").String()
confirmPassword := ctx.Form("confirm_password").String()
fmt.Printf("DEBUG: Form data - username: '%s', email: '%s', password length: %d\n",
username, email, len(userPassword))
formData := map[string]string{
"username": username,
"email": email,
}
if err := validateRegistration(username, email, userPassword, confirmPassword); err != nil {
fmt.Printf("DEBUG: Validation failed: %v\n", err)
setFlashAndFormData(ctx, err.Error(), formData)
ctx.Redirect("/register")
return
}
fmt.Println("DEBUG: Validation passed")
// Check if username already exists
if _, err := users.ByUsername(username); err == nil {
fmt.Printf("DEBUG: Username '%s' already exists\n", username)
setFlashAndFormData(ctx, "Username already exists", formData)
ctx.Redirect("/register")
return
}
fmt.Printf("DEBUG: Username '%s' is available\n", username)
// Check if email already exists
if _, err := users.ByEmail(email); err == nil {
fmt.Printf("DEBUG: Email '%s' already exists\n", email)
setFlashAndFormData(ctx, "Email already registered", formData)
ctx.Redirect("/register")
return
}
fmt.Printf("DEBUG: Email '%s' is available\n", email)
// Create new user
user := users.New()
@ -144,34 +133,26 @@ func processRegister(ctx sushi.Ctx) {
user.ClassID = 1
user.Auth = 1
fmt.Printf("DEBUG: Created user struct with ID: %d\n", user.ID)
// Validate before inserting
if err := user.Validate(); err != nil {
fmt.Printf("DEBUG: User validation failed: %v\n", err)
setFlashAndFormData(ctx, fmt.Sprintf("Invalid user data: %s", err.Error()), formData)
ctx.Redirect("/register")
return
}
fmt.Println("DEBUG: User validation passed")
if err := user.Insert(); err != nil {
fmt.Printf("DEBUG: User insert failed: %v\n", err)
setFlashAndFormData(ctx, "Failed to create account", formData)
ctx.Redirect("/register")
return
}
fmt.Printf("DEBUG: User inserted successfully with ID: %d\n", user.ID)
// Auto-login after registration
ctx.Login(user.ID, user)
fmt.Printf("DEBUG: User logged in with ID: %d\n", user.ID)
// Set success message
sess := ctx.GetCurrentSession()
sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))
fmt.Println("DEBUG: Registration completed successfully")
ctx.Redirect("/")
}

View File

@ -154,7 +154,7 @@ func handleFightAction(ctx sushi.Ctx) {
// Handle fight end states
if result.Ended {
if result.Won {
sess.SetFlash("success", fmt.Sprintf("Victory! You gained rewards!"))
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.")

View File

@ -54,12 +54,18 @@ func Move(ctx sushi.Ctx) {
return
}
err = database.Transaction(func() error {
return database.Update("users", map[string]any{
updateData := map[string]any{
"currently": currently,
"x": newX,
"y": newY,
}, "id", user.ID)
}
if currently == "Fighting" {
updateData["fight_id"] = user.FightID
}
err = database.Transaction(func() error {
return database.Update("users", updateData, "id", user.ID)
})
if err != nil {

View File

@ -77,9 +77,17 @@ func showTown(ctx sushi.Ctx) {
func showInn(ctx sushi.Ctx) {
town := ctx.UserValue("town").(*towns.Town)
sess := ctx.GetCurrentSession()
value, exists := sess.GetFlash("rested")
rested := false
if exists && value == true {
rested = true
}
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
"town": town,
"rested": false,
"rested": rested,
})
}
@ -102,17 +110,14 @@ func rest(ctx sushi.Ctx) {
"tp": user.MaxTP,
}, "id", user.ID)
})
if err != nil {
sess.SetFlash("error", "Failed to rest at inn.")
ctx.Redirect("/town/inn")
return
}
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
"town": town,
"rested": true,
})
sess.SetFlash("rested", true)
ctx.Redirect("/town/inn")
}
func showShop(ctx sushi.Ctx) {

17
main.go
View File

@ -8,6 +8,7 @@ import (
"os/signal"
"path/filepath"
"syscall"
"time"
"dk/internal/control"
"dk/internal/database"
@ -68,13 +69,27 @@ func start(port string) error {
template.InitializeCache(cwd)
authMW := auth.New(getUserByID)
app := sushi.New()
sushi.InitSessions(filepath.Join(cwd, "data/sessions.json"))
app.Use(session.Middleware())
app.Use(auth.Middleware(getUserByID))
app.Use(authMW.Middleware())
app.Use(csrf.Middleware())
app.Use(timing.Middleware())
app.Use(func(ctx sushi.Ctx, next func()) {
if ctx.IsAuthenticated() {
user := ctx.GetCurrentUser().(*users.User)
now := time.Now().Unix()
if (now - user.LastOnline) >= 540 { // 540 seconds = 9 minutes
database.Update("users", map[string]any{
"last_online": now,
}, "id", user.ID)
}
}
next()
})
app.Get("/", routes.Index)