create fights, disable compass
This commit is contained in:
parent
3fd28e0cd9
commit
674cfce506
@ -395,3 +395,10 @@ div.modal {
|
||||
.mt-1 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
img#move-compass-disabled {
|
||||
margin: 0px auto;
|
||||
cursor: not-allowed;
|
||||
filter: grayscale();
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
65
data/fights.json
Normal file
65
data/fights.json
Normal file
@ -0,0 +1,65 @@
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"user_id": 1,
|
||||
"monster_id": 10,
|
||||
"monster_hp": 16,
|
||||
"monster_max_hp": 16,
|
||||
"monster_sleep": 0,
|
||||
"monster_immune": 0,
|
||||
"uber_damage": 0,
|
||||
"uber_defense": 0,
|
||||
"first_strike": false,
|
||||
"turn": 1,
|
||||
"ran_away": false,
|
||||
"victory": false,
|
||||
"won": false,
|
||||
"reward_gold": 0,
|
||||
"reward_exp": 0,
|
||||
"actions": [],
|
||||
"created": 1755215734,
|
||||
"updated": 1755215734
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"user_id": 1,
|
||||
"monster_id": 7,
|
||||
"monster_hp": 12,
|
||||
"monster_max_hp": 12,
|
||||
"monster_sleep": 0,
|
||||
"monster_immune": 1,
|
||||
"uber_damage": 0,
|
||||
"uber_defense": 0,
|
||||
"first_strike": true,
|
||||
"turn": 1,
|
||||
"ran_away": false,
|
||||
"victory": false,
|
||||
"won": false,
|
||||
"reward_gold": 0,
|
||||
"reward_exp": 0,
|
||||
"actions": [],
|
||||
"created": 1755215776,
|
||||
"updated": 1755215776
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"user_id": 1,
|
||||
"monster_id": 8,
|
||||
"monster_hp": 14,
|
||||
"monster_max_hp": 14,
|
||||
"monster_sleep": 0,
|
||||
"monster_immune": 0,
|
||||
"uber_damage": 0,
|
||||
"uber_defense": 0,
|
||||
"first_strike": true,
|
||||
"turn": 1,
|
||||
"ran_away": false,
|
||||
"victory": false,
|
||||
"won": false,
|
||||
"reward_gold": 0,
|
||||
"reward_exp": 0,
|
||||
"actions": [],
|
||||
"created": 1755215777,
|
||||
"updated": 1755215777
|
||||
}
|
||||
]
|
@ -2,9 +2,13 @@ package actions
|
||||
|
||||
import (
|
||||
"dk/internal/models/control"
|
||||
"dk/internal/models/fights"
|
||||
"dk/internal/models/monsters"
|
||||
"dk/internal/models/towns"
|
||||
"dk/internal/models/users"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
type Direction int
|
||||
@ -54,5 +58,67 @@ func Move(user *users.User, dir Direction) (string, int, int, error) {
|
||||
return "In Town", newX, newY, nil
|
||||
}
|
||||
|
||||
// 33% chance to start a fight when moving to non-town location
|
||||
if rand.Float32() < 0.33 {
|
||||
fmt.Println("Fight chance!")
|
||||
monster, err := getRandomMonsterByDistance(newX, newY)
|
||||
if err != nil {
|
||||
fmt.Printf("Finding a monster failed: %s\n", err.Error())
|
||||
return "Exploring", newX, newY, nil // Fall back to exploring if monster selection fails
|
||||
}
|
||||
|
||||
// Create new fight record
|
||||
fight := fights.New(user.ID, monster.ID)
|
||||
fight.MonsterHP = monster.MaxHP
|
||||
fight.MonsterMaxHP = monster.MaxHP
|
||||
fight.MonsterImmune = monster.Immune
|
||||
|
||||
err = fight.Insert()
|
||||
if err != nil {
|
||||
fmt.Printf("Inserting a fight failed: %s\n", err.Error())
|
||||
return "Exploring", newX, newY, nil // Fall back if fight creation fails
|
||||
}
|
||||
|
||||
user.FightID = fight.ID
|
||||
return "Fighting", newX, newY, nil
|
||||
} else {
|
||||
fmt.Println("No fight...")
|
||||
}
|
||||
|
||||
return "Exploring", newX, newY, nil
|
||||
}
|
||||
|
||||
// getRandomMonsterByDistance selects a random monster based on distance from origin
|
||||
func getRandomMonsterByDistance(x, y int) (*monsters.Monster, error) {
|
||||
// Calculate Manhattan distance from origin
|
||||
distance := int(math.Abs(float64(x)) + math.Abs(float64(y)))
|
||||
|
||||
// Determine level range based on distance (every 5 units increases level)
|
||||
minLevel := 1 + (distance / 5)
|
||||
maxLevel := minLevel + 2 // 3-level range
|
||||
|
||||
// Cap the levels at reasonable bounds
|
||||
if minLevel > 50 {
|
||||
minLevel = 50
|
||||
}
|
||||
if maxLevel > 50 {
|
||||
maxLevel = 50
|
||||
}
|
||||
if minLevel < 1 {
|
||||
minLevel = 1
|
||||
}
|
||||
|
||||
// Get monsters in the level range
|
||||
availableMonsters, err := monsters.ByLevelRange(minLevel, maxLevel)
|
||||
if err != nil || len(availableMonsters) == 0 {
|
||||
// Fallback to level 1 monsters if no monsters found
|
||||
availableMonsters, err = monsters.ByLevel(1)
|
||||
if err != nil || len(availableMonsters) == 0 {
|
||||
return nil, fmt.Errorf("no monsters available")
|
||||
}
|
||||
}
|
||||
|
||||
// Pick a random monster from available ones
|
||||
randomIndex := rand.Intn(len(availableMonsters))
|
||||
return availableMonsters[randomIndex], nil
|
||||
}
|
||||
|
59
internal/middleware/fights.go
Normal file
59
internal/middleware/fights.go
Normal file
@ -0,0 +1,59 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"dk/internal/models/users"
|
||||
"dk/internal/router"
|
||||
"strings"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// RequireFighting ensures the user is in a fight when accessing fight routes
|
||||
func RequireFighting() router.Middleware {
|
||||
return func(next router.Handler) router.Handler {
|
||||
return func(ctx router.Ctx, params []string) {
|
||||
user, ok := ctx.UserValue("user").(*users.User)
|
||||
if !ok || user == nil {
|
||||
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
|
||||
ctx.SetBodyString("Not authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsFighting() {
|
||||
ctx.Redirect("/", 303)
|
||||
return
|
||||
}
|
||||
|
||||
next(ctx, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HandleFightRedirect redirects users to appropriate locations based on fight status
|
||||
func HandleFightRedirect() router.Middleware {
|
||||
return func(next router.Handler) router.Handler {
|
||||
return func(ctx router.Ctx, params []string) {
|
||||
user, ok := ctx.UserValue("user").(*users.User)
|
||||
if !ok || user == nil {
|
||||
next(ctx, params)
|
||||
return
|
||||
}
|
||||
|
||||
currentPath := string(ctx.URI().Path())
|
||||
|
||||
// If user is fighting and not on fight page, redirect to fight
|
||||
if user.IsFighting() && !strings.HasPrefix(currentPath, "/fight") {
|
||||
ctx.Redirect("/fight", 303)
|
||||
return
|
||||
}
|
||||
|
||||
// If user is not fighting and on fight page, redirect to home
|
||||
if !user.IsFighting() && strings.HasPrefix(currentPath, "/fight") {
|
||||
ctx.Redirect("/", 303)
|
||||
return
|
||||
}
|
||||
|
||||
next(ctx, params)
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ func New(userID, monsterID int) *Fight {
|
||||
UberDamage: 0,
|
||||
UberDefense: 0,
|
||||
FirstStrike: false,
|
||||
Turn: 1,
|
||||
Turn: 0,
|
||||
RanAway: false,
|
||||
Victory: false,
|
||||
Won: false,
|
||||
@ -71,8 +71,8 @@ func (f *Fight) Validate() error {
|
||||
if f.MonsterID <= 0 {
|
||||
return fmt.Errorf("fight MonsterID must be positive")
|
||||
}
|
||||
if f.Turn < 1 {
|
||||
return fmt.Errorf("fight Turn must be at least 1")
|
||||
if f.Turn < 0 {
|
||||
return fmt.Errorf("fight Turn cannot be negative")
|
||||
}
|
||||
if f.MonsterHP < 0 {
|
||||
return fmt.Errorf("fight MonsterHP cannot be negative")
|
||||
|
@ -33,15 +33,8 @@ func RegisterAuthRoutes(r *router.Router) {
|
||||
// showLogin displays the login form
|
||||
func showLogin(ctx router.Ctx, _ []string) {
|
||||
sess := ctx.UserValue("session").(*session.Session)
|
||||
var errorHTML string
|
||||
var id string
|
||||
|
||||
if flash, exists := sess.GetFlash("error"); exists {
|
||||
if msg, ok := flash.(string); ok {
|
||||
errorHTML = fmt.Sprintf(`<div style="color: red; margin-bottom: 1rem;">%s</div>`, msg)
|
||||
}
|
||||
}
|
||||
|
||||
if formData, exists := sess.Get("form_data"); exists {
|
||||
if data, ok := formData.(map[string]string); ok {
|
||||
id = data["id"]
|
||||
@ -51,8 +44,7 @@ func showLogin(ctx router.Ctx, _ []string) {
|
||||
session.Store(sess)
|
||||
|
||||
components.RenderPage(ctx, "Log In", "auth/login.html", map[string]any{
|
||||
"error_message": errorHTML,
|
||||
"id": id,
|
||||
"id": id,
|
||||
})
|
||||
}
|
||||
|
||||
|
43
internal/routes/fight.go
Normal file
43
internal/routes/fight.go
Normal file
@ -0,0 +1,43 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"dk/internal/auth"
|
||||
"dk/internal/components"
|
||||
"dk/internal/middleware"
|
||||
"dk/internal/models/fights"
|
||||
"dk/internal/models/users"
|
||||
"dk/internal/router"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
func RegisterFightRoutes(r *router.Router) {
|
||||
group := r.Group("/fight")
|
||||
group.Use(auth.RequireAuth())
|
||||
group.Use(middleware.RequireFighting())
|
||||
|
||||
group.Get("/", showFight)
|
||||
}
|
||||
|
||||
func showFight(ctx router.Ctx, _ []string) {
|
||||
user := ctx.UserValue("user").(*users.User)
|
||||
|
||||
fight, err := fights.Find(user.FightID)
|
||||
if err != nil {
|
||||
ctx.SetContentType("text/plain")
|
||||
ctx.SetBodyString("Fight not found")
|
||||
return
|
||||
}
|
||||
|
||||
// If turn 0, determine first strike and advance to turn 1
|
||||
if fight.Turn == 0 {
|
||||
// 50% chance user goes first
|
||||
fight.FirstStrike = rand.Float32() < 0.5
|
||||
fight.Turn = 1
|
||||
fight.Save()
|
||||
}
|
||||
|
||||
components.RenderPage(ctx, "Fighting", "fight/fight.html", map[string]any{
|
||||
"fight": fight,
|
||||
"user": user,
|
||||
})
|
||||
}
|
@ -23,13 +23,23 @@ func Index(ctx router.Ctx, _ []string) {
|
||||
ctx.Redirect("/town", 303)
|
||||
case "Exploring":
|
||||
ctx.Redirect("/explore", 303)
|
||||
case "Fighting":
|
||||
ctx.Redirect("/fight", 303)
|
||||
default:
|
||||
ctx.Redirect("/explore", 303)
|
||||
}
|
||||
}
|
||||
|
||||
func Move(ctx router.Ctx, _ []string) {
|
||||
sess := ctx.UserValue("session").(*session.Session)
|
||||
user := ctx.UserValue("user").(*users.User)
|
||||
|
||||
if user.Currently == "Fighting" {
|
||||
sess.SetFlash("error", "You can't just run from a fight!")
|
||||
ctx.Redirect("/fight", 303)
|
||||
return
|
||||
}
|
||||
|
||||
dir, err := strconv.Atoi(string(ctx.PostArgs().Peek("direction")))
|
||||
if err != nil {
|
||||
ctx.SetContentType("text/plain")
|
||||
@ -46,13 +56,16 @@ func Move(ctx router.Ctx, _ []string) {
|
||||
|
||||
user.Currently = currently
|
||||
user.SetPosition(newX, newY)
|
||||
user.Save()
|
||||
|
||||
if currently == "In Town" {
|
||||
switch currently {
|
||||
case "In Town":
|
||||
ctx.Redirect("/town", 303)
|
||||
return
|
||||
case "Fighting":
|
||||
ctx.Redirect("/fight", 303)
|
||||
default:
|
||||
ctx.Redirect("/explore", 303)
|
||||
}
|
||||
|
||||
ctx.Redirect("/explore", 303)
|
||||
}
|
||||
|
||||
func Explore(ctx router.Ctx, _ []string) {
|
||||
|
@ -50,21 +50,10 @@ func showTown(ctx router.Ctx, _ []string) {
|
||||
}
|
||||
|
||||
func showInn(ctx router.Ctx, _ []string) {
|
||||
sess := ctx.UserValue("session").(*session.Session)
|
||||
var errorHTML string
|
||||
|
||||
if flash, exists := sess.GetFlash("error"); exists {
|
||||
if msg, ok := flash.(string); ok {
|
||||
errorHTML = `<div style="color: red; margin-bottom: 1rem;">` + msg + "</div>"
|
||||
}
|
||||
}
|
||||
|
||||
town := ctx.UserValue("town").(*towns.Town)
|
||||
|
||||
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
|
||||
"town": town,
|
||||
"rested": false,
|
||||
"error_message": errorHTML,
|
||||
"town": town,
|
||||
"rested": false,
|
||||
})
|
||||
}
|
||||
|
||||
|
1
main.go
1
main.go
@ -189,6 +189,7 @@ func start(port string) error {
|
||||
|
||||
routes.RegisterAuthRoutes(r)
|
||||
routes.RegisterTownRoutes(r)
|
||||
routes.RegisterFightRoutes(r)
|
||||
|
||||
// Use current working directory for static files
|
||||
assetsDir := filepath.Join(cwd, "assets")
|
||||
|
5
templates/fight/fight.html
Normal file
5
templates/fight/fight.html
Normal file
@ -0,0 +1,5 @@
|
||||
{include "layout.html"}
|
||||
|
||||
{block "content"}
|
||||
ITS A FIGHT!
|
||||
{/block}
|
@ -12,15 +12,19 @@
|
||||
|
||||
<a href="javascript:open_map_popup()">View Map</a>
|
||||
|
||||
<form id="move-compass" action="/move" method="post" >
|
||||
{csrf}
|
||||
<button id="north" name="direction" value="0">North</button>
|
||||
<div class="mid">
|
||||
<button id="west" name="direction" value="3">West</button>
|
||||
<button id="east" name="direction" value="1">East</button>
|
||||
</div>
|
||||
<button id="south" name="direction" value="2">South</button>
|
||||
</form>
|
||||
{if user.Currently != "Fighting"}
|
||||
<form id="move-compass" action="/move" method="post" >
|
||||
{csrf}
|
||||
<button id="north" name="direction" value="0">North</button>
|
||||
<div class="mid">
|
||||
<button id="west" name="direction" value="3">West</button>
|
||||
<button id="east" name="direction" value="1">East</button>
|
||||
</div>
|
||||
<button id="south" name="direction" value="2">South</button>
|
||||
</form>
|
||||
{else}
|
||||
<img id="move-compass-disabled" src="/assets/images/compass.webp" alt="Can't run!" title="Can't run!">
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
Loading…
x
Reference in New Issue
Block a user