clean up helpers package, remove orderedmaps and clean up list generation
This commit is contained in:
parent
fc30d04ccb
commit
41eed92929
11
data/control.json
Normal file
11
data/control.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"world_size": 200,
|
||||||
|
"open": 1,
|
||||||
|
"admin_email": "",
|
||||||
|
"email_mode": "file",
|
||||||
|
"email_file_path": "data/emails.txt",
|
||||||
|
"smtp_host": "",
|
||||||
|
"smtp_port": "587",
|
||||||
|
"smtp_username": "",
|
||||||
|
"smtp_password": ""
|
||||||
|
}
|
BIN
data/dk.db
BIN
data/dk.db
Binary file not shown.
2
go.mod
2
go.mod
@ -5,7 +5,6 @@ go 1.25.0
|
|||||||
require (
|
require (
|
||||||
git.sharkk.net/Sharkk/Sashimi v1.1.4
|
git.sharkk.net/Sharkk/Sashimi v1.1.4
|
||||||
git.sharkk.net/Sharkk/Sushi v1.2.0
|
git.sharkk.net/Sharkk/Sushi v1.2.0
|
||||||
github.com/valyala/fasthttp v1.65.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -17,6 +16,7 @@ require (
|
|||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasthttp v1.65.0 // indirect
|
||||||
golang.org/x/crypto v0.41.0 // indirect
|
golang.org/x/crypto v0.41.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
golang.org/x/sys v0.35.0 // indirect
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
|
@ -25,15 +25,11 @@ func LeftAside(ctx sushi.Ctx) map[string]any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build owned town maps list
|
// Build owned town maps list
|
||||||
if user.Towns != "" {
|
townList, err := user.GetKnownTowns()
|
||||||
townMap := helpers.NewOrderedMap[int, *towns.Town]()
|
if err != nil {
|
||||||
for _, id := range user.GetTownIDs() {
|
fmt.Printf("User town list error: %s\n", err.Error())
|
||||||
if town, err := towns.Find(id); err == nil {
|
|
||||||
townMap.Set(id, town)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data["_towns"] = townMap.ToSlice()
|
|
||||||
}
|
}
|
||||||
|
data["_towns"] = townList
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@ -65,16 +61,11 @@ func RightAside(ctx sushi.Ctx) map[string]any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build known healing spells list
|
// Build known healing spells list
|
||||||
userSpells, err := user.GetSpells()
|
healingSpells, err := user.GetHealingSpells()
|
||||||
if err == nil {
|
if err != nil {
|
||||||
spellMap := helpers.NewOrderedMap[int, string]()
|
fmt.Printf("User healing spells list error: %s\n", err.Error())
|
||||||
for _, spell := range userSpells {
|
|
||||||
if spell.IsHeal() {
|
|
||||||
spellMap.Set(spell.ID, spell.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data["_spells"] = spellMap.ToSlice()
|
|
||||||
}
|
}
|
||||||
|
data["_spells"] = healingSpells
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package exp
|
|
||||||
|
|
||||||
func Calc(level int) int {
|
|
||||||
return level * level * level
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import "github.com/valyala/fasthttp"
|
|
||||||
|
|
||||||
// IsHTTPS tries to determine if the current request context is over HTTPS
|
|
||||||
func IsHTTPS(ctx *fasthttp.RequestCtx) bool {
|
|
||||||
return ctx.IsTLS() ||
|
|
||||||
string(ctx.Request.Header.Peek("X-Forwarded-Proto")) == "https" ||
|
|
||||||
string(ctx.Request.Header.Peek("X-Forwarded-Scheme")) == "https"
|
|
||||||
}
|
|
@ -8,3 +8,8 @@ func ClampPct(num, denom, minimum, maximum float64) float64 {
|
|||||||
}
|
}
|
||||||
return max(minimum, min(maximum, (num/denom)*100))
|
return max(minimum, min(maximum, (num/denom)*100))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExpAtLevel calculates the needed total EXP for the given level.
|
||||||
|
func ExpAtLevel(level int) int {
|
||||||
|
return level * level * level
|
||||||
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
type OrderedMap[K comparable, V any] struct {
|
|
||||||
keys []K
|
|
||||||
data map[K]V
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOrderedMap[K comparable, V any]() *OrderedMap[K, V] {
|
|
||||||
return &OrderedMap[K, V]{
|
|
||||||
keys: make([]K, 0),
|
|
||||||
data: make(map[K]V),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (om *OrderedMap[K, V]) Set(key K, value V) {
|
|
||||||
if _, exists := om.data[key]; !exists {
|
|
||||||
om.keys = append(om.keys, key)
|
|
||||||
}
|
|
||||||
om.data[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (om *OrderedMap[K, V]) Range(fn func(K, V) bool) {
|
|
||||||
for _, key := range om.keys {
|
|
||||||
if !fn(key, om.data[key]) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (om *OrderedMap[K, V]) ToSlice() []V {
|
|
||||||
result := make([]V, 0, len(om.keys))
|
|
||||||
for _, key := range om.keys {
|
|
||||||
result = append(result, om.data[key])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
@ -135,6 +135,17 @@ func UserSpells(userID int) ([]*Spell, error) {
|
|||||||
return spells, err
|
return spells, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UserHealingSpells(userID int) ([]*Spell, error) {
|
||||||
|
var spells []*Spell
|
||||||
|
query := `
|
||||||
|
SELECT s.* FROM spells s
|
||||||
|
JOIN user_spells us ON s.id = us.spell_id
|
||||||
|
WHERE us.user_id = %d AND s.type = 0
|
||||||
|
ORDER BY s.mp ASC, s.id ASC`
|
||||||
|
err := database.Select(&spells, query, userID)
|
||||||
|
return spells, err
|
||||||
|
}
|
||||||
|
|
||||||
func GrantSpell(userID, spellID int) error {
|
func GrantSpell(userID, spellID int) error {
|
||||||
return database.Exec("INSERT OR IGNORE INTO user_spells (user_id, spell_id) VALUES (%d, %d)", userID, spellID)
|
return database.Exec("INSERT OR IGNORE INTO user_spells (user_id, spell_id) VALUES (%d, %d)", userID, spellID)
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"dk/internal/database"
|
"dk/internal/database"
|
||||||
"dk/internal/helpers"
|
"dk/internal/helpers"
|
||||||
|
"dk/internal/models/items"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Town represents a town in the game
|
// Town represents a town in the game
|
||||||
@ -142,16 +143,16 @@ func ByDistance(fromX, fromY, maxDistance int) ([]*Town, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
func (t *Town) GetShopItems() []int {
|
func (t *Town) GetShopItemIDs() []int {
|
||||||
return helpers.StringToInts(t.ShopList)
|
return helpers.StringToInts(t.ShopList)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Town) SetShopItems(items []int) {
|
func (t *Town) SetShopItemIDs(items []int) {
|
||||||
t.ShopList = helpers.IntsToString(items)
|
t.ShopList = helpers.IntsToString(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Town) HasShopItem(itemID int) bool {
|
func (t *Town) HasShopItem(itemID int) bool {
|
||||||
return slices.Contains(t.GetShopItems(), itemID)
|
return slices.Contains(t.GetShopItemIDs(), itemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Town) DistanceFromSquared(x, y int) float64 {
|
func (t *Town) DistanceFromSquared(x, y int) float64 {
|
||||||
@ -181,7 +182,7 @@ func (t *Town) CanAffordTeleport(gold int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Town) HasShop() bool {
|
func (t *Town) HasShop() bool {
|
||||||
return len(t.GetShopItems()) > 0
|
return len(t.GetShopItemIDs()) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Town) GetPosition() (int, int) {
|
func (t *Town) GetPosition() (int, int) {
|
||||||
@ -192,3 +193,19 @@ func (t *Town) SetPosition(x, y int) {
|
|||||||
t.X = x
|
t.X = x
|
||||||
t.Y = y
|
t.Y = y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Town) GetShopItems() ([]*items.Item, error) {
|
||||||
|
itemIDs := t.GetShopItemIDs()
|
||||||
|
result := make([]*items.Item, 0, len(itemIDs))
|
||||||
|
|
||||||
|
for _, itemID := range itemIDs {
|
||||||
|
item, err := items.Find(itemID)
|
||||||
|
if err != nil {
|
||||||
|
// Skip invalid item IDs rather than failing entirely
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
@ -8,9 +8,9 @@ import (
|
|||||||
|
|
||||||
"dk/internal/database"
|
"dk/internal/database"
|
||||||
"dk/internal/helpers"
|
"dk/internal/helpers"
|
||||||
"dk/internal/helpers/exp"
|
|
||||||
"dk/internal/models/classes"
|
"dk/internal/models/classes"
|
||||||
"dk/internal/models/spells"
|
"dk/internal/models/spells"
|
||||||
|
"dk/internal/models/towns"
|
||||||
)
|
)
|
||||||
|
|
||||||
// User represents a user in the game
|
// User represents a user in the game
|
||||||
@ -213,6 +213,10 @@ func (u *User) GetSpells() ([]*spells.Spell, error) {
|
|||||||
return spells.UserSpells(u.ID)
|
return spells.UserSpells(u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) GetHealingSpells() ([]*spells.Spell, error) {
|
||||||
|
return spells.UserHealingSpells(u.ID)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *User) HasSpell(spellID int) bool {
|
func (u *User) HasSpell(spellID int) bool {
|
||||||
return spells.HasSpell(u.ID, spellID)
|
return spells.HasSpell(u.ID, spellID)
|
||||||
}
|
}
|
||||||
@ -288,7 +292,7 @@ func (u *User) SetPosition(x, y int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) ExpNeededForNextLevel() int {
|
func (u *User) ExpNeededForNextLevel() int {
|
||||||
return exp.Calc(u.Level + 1)
|
return helpers.ExpAtLevel(u.Level + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GrantExp(expAmount int) map[string]any {
|
func (u *User) GrantExp(expAmount int) map[string]any {
|
||||||
@ -324,7 +328,7 @@ func (u *User) CalculateLevelUp(expGain int) (newLevel, newStr, newDex, newExp i
|
|||||||
totalExp := u.Exp + expGain
|
totalExp := u.Exp + expGain
|
||||||
|
|
||||||
for {
|
for {
|
||||||
expNeeded := exp.Calc(level + 1)
|
expNeeded := helpers.ExpAtLevel(level + 1)
|
||||||
if totalExp < expNeeded {
|
if totalExp < expNeeded {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -343,7 +347,7 @@ func (u *User) ExpProgress() float64 {
|
|||||||
return float64(u.Exp) / float64(u.ExpNeededForNextLevel()) * 100
|
return float64(u.Exp) / float64(u.ExpNeededForNextLevel()) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLevelExp := exp.Calc(u.Level)
|
currentLevelExp := helpers.ExpAtLevel(u.Level)
|
||||||
nextLevelExp := u.ExpNeededForNextLevel()
|
nextLevelExp := u.ExpNeededForNextLevel()
|
||||||
progressExp := u.Exp
|
progressExp := u.Exp
|
||||||
|
|
||||||
@ -375,3 +379,19 @@ func (u *User) learnSpellsForLevel(level int) error {
|
|||||||
|
|
||||||
return u.GrantSpells(spellIDs)
|
return u.GrantSpells(spellIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) GetKnownTowns() ([]*towns.Town, error) {
|
||||||
|
townIDs := u.GetTownIDs()
|
||||||
|
result := make([]*towns.Town, 0, len(townIDs))
|
||||||
|
|
||||||
|
for _, townID := range townIDs {
|
||||||
|
town, err := towns.Find(townID)
|
||||||
|
if err != nil {
|
||||||
|
// Skip invalid town IDs rather than failing entirely
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, town)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"dk/internal/models/towns"
|
"dk/internal/models/towns"
|
||||||
"dk/internal/models/users"
|
"dk/internal/models/users"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
|
|
||||||
sushi "git.sharkk.net/Sharkk/Sushi"
|
sushi "git.sharkk.net/Sharkk/Sushi"
|
||||||
"git.sharkk.net/Sharkk/Sushi/auth"
|
"git.sharkk.net/Sharkk/Sushi/auth"
|
||||||
@ -131,20 +130,14 @@ func showShop(ctx sushi.Ctx) {
|
|||||||
|
|
||||||
town := ctx.UserValue("town").(*towns.Town)
|
town := ctx.UserValue("town").(*towns.Town)
|
||||||
|
|
||||||
var itemlist []*items.Item
|
itemList, err := town.GetShopItems()
|
||||||
if town.ShopList != "" {
|
if err != nil {
|
||||||
itemMap := helpers.NewOrderedMap[int, *items.Item]()
|
fmt.Printf("Town shop list error: %s\n", err.Error())
|
||||||
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{
|
components.RenderPage(ctx, town.Name+" Shop", "town/shop.html", map[string]any{
|
||||||
"town": town,
|
"town": town,
|
||||||
"itemlist": itemlist,
|
"itemlist": itemList,
|
||||||
"error_message": errorHTML,
|
"error_message": errorHTML,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -155,7 +148,7 @@ func buyItem(ctx sushi.Ctx) {
|
|||||||
id := ctx.Param("id").Int()
|
id := ctx.Param("id").Int()
|
||||||
|
|
||||||
town := ctx.UserValue("town").(*towns.Town)
|
town := ctx.UserValue("town").(*towns.Town)
|
||||||
if !slices.Contains(town.GetShopItems(), id) {
|
if !town.HasShopItem(id) {
|
||||||
sess.SetFlash("error", "The item doesn't exist in this shop.")
|
sess.SetFlash("error", "The item doesn't exist in this shop.")
|
||||||
ctx.Redirect("/town/shop")
|
ctx.Redirect("/town/shop")
|
||||||
return
|
return
|
||||||
@ -216,36 +209,12 @@ func showMaps(ctx sushi.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
town := ctx.UserValue("town").(*towns.Town)
|
town := ctx.UserValue("town").(*towns.Town)
|
||||||
user := ctx.GetCurrentUser().(*users.User)
|
|
||||||
|
|
||||||
maplist := helpers.NewOrderedMap[int, Map]()
|
|
||||||
towns, _ := towns.All()
|
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{
|
components.RenderPage(ctx, town.Name+" Maps", "town/maps.html", map[string]any{
|
||||||
"town": town,
|
"town": town,
|
||||||
"maplist": maplist.ToSlice(),
|
"towns": towns,
|
||||||
"error_message": errorHTML,
|
"error_message": errorHTML,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
5
main.go
5
main.go
@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"dk/internal/control"
|
"dk/internal/control"
|
||||||
"dk/internal/database"
|
"dk/internal/database"
|
||||||
|
"dk/internal/helpers/email"
|
||||||
"dk/internal/helpers/markdown"
|
"dk/internal/helpers/markdown"
|
||||||
"dk/internal/models/users"
|
"dk/internal/models/users"
|
||||||
"dk/internal/routes"
|
"dk/internal/routes"
|
||||||
@ -142,10 +143,14 @@ func start(port string) error {
|
|||||||
|
|
||||||
control.Init(filepath.Join(cwd, "data/control.json"))
|
control.Init(filepath.Join(cwd, "data/control.json"))
|
||||||
defer control.Save()
|
defer control.Save()
|
||||||
|
controls := control.Get()
|
||||||
|
|
||||||
template.InitializeCache(cwd)
|
template.InitializeCache(cwd)
|
||||||
template.RegisterFunc("md", markdown.MarkdownTemplateFunc)
|
template.RegisterFunc("md", markdown.MarkdownTemplateFunc)
|
||||||
|
|
||||||
|
email.Init(controls.EmailMode, controls.EmailFilePath,
|
||||||
|
controls.SMTPHost, controls.SMTPPort, controls.SMTPUsername, controls.SMTPPassword)
|
||||||
|
|
||||||
authMW := auth.New(getUserByID)
|
authMW := auth.New(getUserByID)
|
||||||
|
|
||||||
app := sushi.New()
|
app := sushi.New()
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
<h5>Town Maps</h5>
|
<h5>Town Maps</h5>
|
||||||
{if #_towns > 0}
|
{if #_towns > 0}
|
||||||
{for t in _towns}
|
{for t in _towns}
|
||||||
|
{if user.HasTownMap(t.ID)}
|
||||||
{if town != nil and t.Name == town.Name}
|
{if town != nil and t.Name == town.Name}
|
||||||
<span>{t.Name} <i>(here)</i></span>
|
<span>{t.Name} <i>(here)</i></span>
|
||||||
{else}
|
{else}
|
||||||
@ -38,6 +39,7 @@
|
|||||||
<a class="teleport-link" href="/teleport/{t.ID}" data-town="{t.Name}" data-cost="{t.TPCost}">{t.Name}</a>
|
<a class="teleport-link" href="/teleport/{t.ID}" data-town="{t.Name}" data-cost="{t.TPCost}">{t.Name}</a>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
{/for}
|
{/for}
|
||||||
{else}
|
{else}
|
||||||
<i>No town maps</i>
|
<i>No town maps</i>
|
||||||
|
@ -76,8 +76,8 @@
|
|||||||
<section>
|
<section>
|
||||||
<h5>Spells</h5>
|
<h5>Spells</h5>
|
||||||
{if #_spells > 0}
|
{if #_spells > 0}
|
||||||
{for id,name in _spells}
|
{for spell in _spells}
|
||||||
<a href="#">{name}</a>
|
<a href="#">{spell.Name}</a>
|
||||||
{/for}
|
{/for}
|
||||||
{else}
|
{else}
|
||||||
<i>No known healing spells</i>
|
<i>No known healing spells</i>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user