286 lines
6.4 KiB
Go
286 lines
6.4 KiB
Go
package routes
|
|
|
|
import (
|
|
"dk/internal/actions"
|
|
"dk/internal/components"
|
|
"dk/internal/database"
|
|
"dk/internal/helpers"
|
|
"dk/internal/models/items"
|
|
"dk/internal/models/towns"
|
|
"dk/internal/models/users"
|
|
"fmt"
|
|
"slices"
|
|
|
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
|
)
|
|
|
|
// Map acts as a representation of owned/unowned maps in the town stores.
|
|
type Map struct {
|
|
ID int
|
|
Name string
|
|
Cost int
|
|
Owned bool
|
|
X int
|
|
Y int
|
|
TP int
|
|
}
|
|
|
|
func RegisterTownRoutes(app *sushi.App) {
|
|
group := app.Group("/town")
|
|
group.Use(auth.RequireAuth("/login"))
|
|
group.Use(requireTown())
|
|
|
|
group.Get("/", showTown)
|
|
group.Get("/inn", showInn)
|
|
group.Post("/inn", rest)
|
|
group.Get("/shop", showShop)
|
|
group.Get("/shop/buy/:id", buyItem)
|
|
group.Get("/maps", showMaps)
|
|
group.Get("/maps/buy/:id", buyMap)
|
|
}
|
|
|
|
// requireTown middleware ensures the user is in a town
|
|
func requireTown() 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.Currently != "In Town" {
|
|
ctx.SendError(403, "You must be in town")
|
|
return
|
|
}
|
|
|
|
town, err := towns.ByCoords(userModel.X, userModel.Y)
|
|
if err != nil || town == nil || town.ID == 0 {
|
|
ctx.SendError(403, fmt.Sprintf("Invalid town location (%d, %d)", userModel.X, userModel.Y))
|
|
return
|
|
}
|
|
|
|
ctx.SetUserValue("town", town)
|
|
next()
|
|
}
|
|
}
|
|
|
|
func showTown(ctx sushi.Ctx) {
|
|
town := ctx.UserValue("town").(*towns.Town)
|
|
components.RenderPage(ctx, town.Name, "town/town.html", map[string]any{
|
|
"town": town,
|
|
"newscontent": components.GenerateTownNews(),
|
|
"whosonline": components.GenerateTownWhosOnline(),
|
|
})
|
|
}
|
|
|
|
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": rested,
|
|
})
|
|
}
|
|
|
|
func rest(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
town := ctx.UserValue("town").(*towns.Town)
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
|
|
if user.Gold < town.InnCost {
|
|
sess.SetFlash("error", "You can't afford to stay here tonight.")
|
|
ctx.Redirect("/town/inn")
|
|
return
|
|
}
|
|
|
|
err := database.Transaction(func() error {
|
|
return database.Update("users", map[string]any{
|
|
"gold": user.Gold - town.InnCost,
|
|
"hp": user.MaxHP,
|
|
"mp": user.MaxMP,
|
|
"tp": user.MaxTP,
|
|
}, "id", user.ID)
|
|
})
|
|
if err != nil {
|
|
sess.SetFlash("error", "Failed to rest at inn.")
|
|
ctx.Redirect("/town/inn")
|
|
return
|
|
}
|
|
|
|
sess.SetFlash("rested", true)
|
|
ctx.Redirect("/town/inn")
|
|
}
|
|
|
|
func showShop(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
var errorHTML string
|
|
|
|
errorMsg := sess.GetFlashMessage("error")
|
|
if errorMsg != "" {
|
|
errorHTML = `<div style="color: red; margin-bottom: 1rem;">` + errorMsg + "</div>"
|
|
}
|
|
|
|
town := ctx.UserValue("town").(*towns.Town)
|
|
|
|
var itemlist []*items.Item
|
|
if town.ShopList != "" {
|
|
itemMap := helpers.NewOrderedMap[int, *items.Item]()
|
|
for _, id := range town.GetShopItems() {
|
|
if item, err := items.Find(id); err == nil {
|
|
itemMap.Set(id, item)
|
|
}
|
|
}
|
|
itemlist = itemMap.ToSlice()
|
|
}
|
|
|
|
components.RenderPage(ctx, town.Name+" Shop", "town/shop.html", map[string]any{
|
|
"town": town,
|
|
"itemlist": itemlist,
|
|
"error_message": errorHTML,
|
|
})
|
|
}
|
|
|
|
func buyItem(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
|
|
id := ctx.Param("id").Int()
|
|
|
|
town := ctx.UserValue("town").(*towns.Town)
|
|
if !slices.Contains(town.GetShopItems(), id) {
|
|
sess.SetFlash("error", "The item doesn't exist in this shop.")
|
|
ctx.Redirect("/town/shop")
|
|
return
|
|
}
|
|
|
|
item, err := items.Find(id)
|
|
if err != nil {
|
|
sess.SetFlash("error", "Error purchasing item; "+err.Error())
|
|
ctx.Redirect("/town/shop")
|
|
return
|
|
}
|
|
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
if user.Gold < item.Value {
|
|
sess.SetFlash("error", "You don't have enough gold to buy "+item.Name)
|
|
ctx.Redirect("/town/shop")
|
|
return
|
|
}
|
|
|
|
// Get equipment updates from actions
|
|
equipUpdates := actions.UserEquipItem(user, item)
|
|
|
|
err = database.Transaction(func() error {
|
|
// Start with gold deduction
|
|
updates := map[string]any{
|
|
"gold": user.Gold - item.Value,
|
|
}
|
|
|
|
// Add equipment updates
|
|
for field, value := range equipUpdates {
|
|
updates[field] = value
|
|
}
|
|
|
|
return database.Update("users", updates, "id", user.ID)
|
|
})
|
|
|
|
if err != nil {
|
|
sess.SetFlash("error", "Failed to purchase item.")
|
|
ctx.Redirect("/town/shop")
|
|
return
|
|
}
|
|
|
|
ctx.Redirect("/town/shop")
|
|
}
|
|
|
|
func showMaps(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
var errorHTML string
|
|
|
|
errorMsg := sess.GetFlashMessage("error")
|
|
if errorMsg != "" {
|
|
errorHTML = `<div style="color: red; margin-bottom: 1rem;">` + errorMsg + "</div>"
|
|
}
|
|
|
|
town := ctx.UserValue("town").(*towns.Town)
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
|
|
maplist := helpers.NewOrderedMap[int, Map]()
|
|
towns, _ := towns.All()
|
|
var mapped []int
|
|
if user.Towns != "" {
|
|
mapped = user.GetTownIDs()
|
|
}
|
|
for _, town := range towns {
|
|
var owned bool
|
|
if mapped != nil && slices.Contains(mapped, town.ID) {
|
|
owned = true
|
|
} else {
|
|
owned = false
|
|
}
|
|
|
|
maplist.Set(town.ID, Map{
|
|
ID: town.ID,
|
|
Name: town.Name,
|
|
Cost: town.MapCost,
|
|
Owned: owned,
|
|
X: town.X,
|
|
Y: town.Y,
|
|
TP: town.TPCost,
|
|
})
|
|
}
|
|
|
|
components.RenderPage(ctx, town.Name+" Maps", "town/maps.html", map[string]any{
|
|
"town": town,
|
|
"maplist": maplist.ToSlice(),
|
|
"error_message": errorHTML,
|
|
})
|
|
}
|
|
|
|
func buyMap(ctx sushi.Ctx) {
|
|
sess := ctx.GetCurrentSession()
|
|
|
|
id := ctx.Param("id").Int()
|
|
|
|
mapped, err := towns.Find(id)
|
|
if err != nil {
|
|
sess.SetFlash("error", "Error purchasing map; "+err.Error())
|
|
ctx.Redirect("/town/maps")
|
|
return
|
|
}
|
|
|
|
user := ctx.GetCurrentUser().(*users.User)
|
|
if user.Gold < mapped.MapCost {
|
|
sess.SetFlash("error", "You don't have enough gold to buy the map to "+mapped.Name)
|
|
ctx.Redirect("/town/maps")
|
|
return
|
|
}
|
|
|
|
townIDs := user.GetTownIDs()
|
|
townIDs = append(townIDs, id)
|
|
newTownsString := helpers.IntsToString(townIDs)
|
|
|
|
err = database.Transaction(func() error {
|
|
return database.Update("users", map[string]any{
|
|
"gold": user.Gold - mapped.MapCost,
|
|
"towns": newTownsString,
|
|
}, "id", user.ID)
|
|
})
|
|
|
|
if err != nil {
|
|
sess.SetFlash("error", "Failed to purchase map.")
|
|
ctx.Redirect("/town/maps")
|
|
return
|
|
}
|
|
|
|
ctx.Redirect("/town/maps")
|
|
}
|