fix model saving, add resting
This commit is contained in:
parent
56aa3afd4f
commit
e3a125f6cf
@ -321,3 +321,7 @@ div#statbars {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
@ -27,12 +27,12 @@ type BaseModel struct {
|
||||
// Set uses reflection to set a field and track changes
|
||||
func Set(model Model, field string, value any) error {
|
||||
v := reflect.ValueOf(model).Elem()
|
||||
fieldVal := v.FieldByName(field)
|
||||
t := v.Type()
|
||||
|
||||
fieldVal := v.FieldByName(field)
|
||||
if !fieldVal.IsValid() {
|
||||
return fmt.Errorf("field %s does not exist", field)
|
||||
}
|
||||
|
||||
if !fieldVal.CanSet() {
|
||||
return fmt.Errorf("field %s cannot be set", field)
|
||||
}
|
||||
@ -42,13 +42,17 @@ func Set(model Model, field string, value any) error {
|
||||
|
||||
// Only set if value has changed
|
||||
if !reflect.DeepEqual(currentVal, value) {
|
||||
// Convert value to correct type
|
||||
newVal := reflect.ValueOf(value)
|
||||
if newVal.Type().ConvertibleTo(fieldVal.Type()) {
|
||||
fieldVal.Set(newVal.Convert(fieldVal.Type()))
|
||||
|
||||
// Convert field name to snake_case for database
|
||||
dbField := toSnakeCase(field)
|
||||
// Get db column name from struct tag
|
||||
structField, _ := t.FieldByName(field)
|
||||
dbField := structField.Tag.Get("db")
|
||||
if dbField == "" {
|
||||
dbField = toSnakeCase(field) // fallback
|
||||
}
|
||||
|
||||
model.SetDirty(dbField, value)
|
||||
} else {
|
||||
return fmt.Errorf("cannot convert %T to %s", value, fieldVal.Type())
|
||||
@ -63,7 +67,10 @@ func toSnakeCase(s string) string {
|
||||
var result strings.Builder
|
||||
for i, r := range s {
|
||||
if i > 0 && r >= 'A' && r <= 'Z' {
|
||||
result.WriteByte('_')
|
||||
prev := rune(s[i-1])
|
||||
if prev < 'A' || prev > 'Z' {
|
||||
result.WriteByte('_')
|
||||
}
|
||||
}
|
||||
if r >= 'A' && r <= 'Z' {
|
||||
result.WriteRune(r - 'A' + 'a')
|
||||
|
@ -1,10 +1,13 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"dk/internal/auth"
|
||||
"dk/internal/middleware"
|
||||
"dk/internal/router"
|
||||
"dk/internal/template/components"
|
||||
"dk/internal/towns"
|
||||
"dk/internal/users"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func RegisterTownRoutes(r *router.Router) {
|
||||
@ -14,6 +17,7 @@ func RegisterTownRoutes(r *router.Router) {
|
||||
|
||||
group.Get("/", showTown)
|
||||
group.Get("/inn", showInn)
|
||||
group.WithMiddleware(middleware.CSRF(auth.Manager)).Post("/inn", rest)
|
||||
}
|
||||
|
||||
func showTown(ctx router.Ctx, _ []string) {
|
||||
@ -28,6 +32,29 @@ func showTown(ctx router.Ctx, _ []string) {
|
||||
func showInn(ctx router.Ctx, _ []string) {
|
||||
town := ctx.UserValue("town").(*towns.Town)
|
||||
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
|
||||
"town": town,
|
||||
"town": town,
|
||||
"rested": false,
|
||||
})
|
||||
}
|
||||
|
||||
func rest(ctx router.Ctx, _ []string) {
|
||||
town := ctx.UserValue("town").(*towns.Town)
|
||||
user := ctx.UserValue("user").(*users.User)
|
||||
|
||||
if user.Gold < town.InnCost {
|
||||
fmt.Println("Cant afford")
|
||||
ctx.Redirect("/town", 303)
|
||||
return
|
||||
}
|
||||
|
||||
user.Set("Gold", user.Gold-town.InnCost)
|
||||
user.Set("HP", user.MaxHP)
|
||||
user.Set("MP", user.MaxMP)
|
||||
user.Set("TP", user.MaxTP)
|
||||
user.Save()
|
||||
|
||||
components.RenderPage(ctx, town.Name+" Inn", "town/inn.html", map[string]any{
|
||||
"town": town,
|
||||
"rested": true,
|
||||
})
|
||||
}
|
||||
|
@ -557,17 +557,41 @@ func (t *Template) processConditionals(content string, data map[string]any) stri
|
||||
condition := strings.TrimSpace(result[start+4 : headerEnd]) // Skip "{if "
|
||||
|
||||
contentStart := headerEnd + 1
|
||||
endTag := "{/if}"
|
||||
contentEnd := strings.Index(result[contentStart:], endTag)
|
||||
|
||||
// Find matching {/if} by tracking nesting level
|
||||
nestLevel := 1
|
||||
pos := contentStart
|
||||
contentEnd := -1
|
||||
|
||||
for pos < len(result) && nestLevel > 0 {
|
||||
ifStart := strings.Index(result[pos:], "{if ")
|
||||
endStart := strings.Index(result[pos:], "{/if}")
|
||||
|
||||
if ifStart != -1 && (endStart == -1 || ifStart < endStart) {
|
||||
// Found nested {if}
|
||||
nestLevel++
|
||||
pos += ifStart + 4
|
||||
} else if endStart != -1 {
|
||||
// Found {/if}
|
||||
nestLevel--
|
||||
if nestLevel == 0 {
|
||||
contentEnd = pos + endStart
|
||||
break
|
||||
}
|
||||
pos += endStart + 5
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if contentEnd == -1 {
|
||||
break
|
||||
}
|
||||
contentEnd += contentStart
|
||||
|
||||
ifContent := result[contentStart:contentEnd]
|
||||
|
||||
// Check for else clause
|
||||
elseStart := strings.Index(ifContent, "{else}")
|
||||
// Check for else clause at the same nesting level
|
||||
elseStart := t.findElseAtLevel(ifContent)
|
||||
var trueContent, falseContent string
|
||||
if elseStart != -1 {
|
||||
trueContent = ifContent[:elseStart]
|
||||
@ -588,12 +612,61 @@ func (t *Template) processConditionals(content string, data map[string]any) stri
|
||||
selectedContent = t.processLoops(selectedContent, data)
|
||||
selectedContent = t.processConditionals(selectedContent, data)
|
||||
|
||||
result = result[:start] + selectedContent + result[contentEnd+len(endTag):]
|
||||
result = result[:start] + selectedContent + result[contentEnd+5:] // +5 for "{/if}"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// findElseAtLevel finds {else} at the top level (not nested)
|
||||
func (t *Template) findElseAtLevel(content string) int {
|
||||
nestLevel := 0
|
||||
pos := 0
|
||||
|
||||
for pos < len(content) {
|
||||
ifStart := strings.Index(content[pos:], "{if ")
|
||||
elseStart := strings.Index(content[pos:], "{else}")
|
||||
endStart := strings.Index(content[pos:], "{/if}")
|
||||
|
||||
// Find the earliest occurrence
|
||||
earliest := -1
|
||||
var tag string
|
||||
|
||||
if ifStart != -1 && (earliest == -1 || ifStart < earliest-pos) {
|
||||
earliest = pos + ifStart
|
||||
tag = "if"
|
||||
}
|
||||
if elseStart != -1 && (earliest == -1 || elseStart < earliest-pos) {
|
||||
earliest = pos + elseStart
|
||||
tag = "else"
|
||||
}
|
||||
if endStart != -1 && (earliest == -1 || endStart < earliest-pos) {
|
||||
earliest = pos + endStart
|
||||
tag = "end"
|
||||
}
|
||||
|
||||
if earliest == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case "if":
|
||||
nestLevel++
|
||||
pos = earliest + 4
|
||||
case "else":
|
||||
if nestLevel == 0 {
|
||||
return earliest
|
||||
}
|
||||
pos = earliest + 6
|
||||
case "end":
|
||||
nestLevel--
|
||||
pos = earliest + 5
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// evaluateCondition evaluates simple conditions like "user.name", "count > 0", "items"
|
||||
func (t *Template) evaluateCondition(condition string, data map[string]any) bool {
|
||||
condition = strings.TrimSpace(condition)
|
||||
|
@ -4,16 +4,22 @@
|
||||
<div class="town inn">
|
||||
<div class="title"><h3>{town.Name} Inn</h3></div>
|
||||
|
||||
{if user.Gold < town.InnCost}
|
||||
<p>You do not have enough gold to stay at this Inn tonight.</p>
|
||||
{if rested}
|
||||
<p>You wake up feeling refreshed and ready for action!</p>
|
||||
<p>You may return to <a href="/town">town</a>, or use the compass on the left to explore.</p>
|
||||
{else}
|
||||
Resting at the inn will refill your current HP, MP, and TP to their maximum levels.<br><br>
|
||||
A night's sleep at this Inn will cost you <b>{town.InnCost} gold</b>. Is that ok?<br><br>
|
||||
<form action="/town/inn" method="post">
|
||||
<button class="btn" type="submit">Yes</button>
|
||||
{if user.Gold < town.InnCost}
|
||||
<p>You do not have enough gold to stay at this inn tonight.</p>
|
||||
<p>You may return to <a href="/town">town</a>, or use the compass on the left to explore.</p>
|
||||
{else}
|
||||
Resting at the inn will refill your current HP, MP, and TP to their maximum levels.<br><br>
|
||||
A night's sleep at this inn will cost you <b>{town.InnCost} gold</b>. Is that ok?<br><br>
|
||||
<form action="/town/inn" method="post" class="inline-block">
|
||||
{csrf}
|
||||
<button class="btn" type="submit">Yes</button>
|
||||
</form>
|
||||
<a href="/town"><button class="btn">No</button></a>
|
||||
</form>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/block}
|
Loading…
x
Reference in New Issue
Block a user