add air config, some style fixes, finish first inn page

This commit is contained in:
Sky Johnson 2025-08-12 14:16:18 -05:00
parent ae7b4a3066
commit 56aa3afd4f
8 changed files with 120 additions and 39 deletions

52
.air.toml Normal file
View File

@ -0,0 +1,52 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "templates"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
silent = false
time = false
[misc]
clean_on_exit = false
[proxy]
app_port = 0
enabled = false
proxy_port = 0
[screen]
clear_on_rebuild = false
keep_scroll = true

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
/dk.db /dk.db
/dk.db-* /dk.db-*
/sessions.json /sessions.json
/tmp

View File

@ -245,16 +245,16 @@ form.standard {
} }
div.town { div.town {
& > div:not(:last-child) { & > section:not(:last-child) {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
& > div.split { & > section.split {
width: 100%; width: 100%;
display: flex; display: flex;
gap: 1rem; gap: 1rem;
& > div { & > section {
width: 100%; width: 100%;
} }
} }

View File

@ -13,6 +13,7 @@ func RegisterTownRoutes(r *router.Router) {
group.Use(middleware.RequireTown()) group.Use(middleware.RequireTown())
group.Get("/", showTown) group.Get("/", showTown)
group.Get("/inn", showInn)
} }
func showTown(ctx router.Ctx, _ []string) { func showTown(ctx router.Ctx, _ []string) {
@ -23,3 +24,10 @@ func showTown(ctx router.Ctx, _ []string) {
"whosonline": components.GenerateTownWhosOnline(), "whosonline": components.GenerateTownWhosOnline(),
}) })
} }
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,
})
}

View File

@ -43,15 +43,15 @@ func RightAside(ctx router.Ctx) map[string]any {
} }
hpPct := helpers.ClampPct(float64(user.HP), float64(user.MaxHP), 0, 100) hpPct := helpers.ClampPct(float64(user.HP), float64(user.MaxHP), 0, 100)
data["hpPct"] = hpPct data["hppct"] = hpPct
data["mpPct"] = helpers.ClampPct(float64(user.MP), float64(user.MaxMP), 0, 100) data["mppct"] = helpers.ClampPct(float64(user.MP), float64(user.MaxMP), 0, 100)
data["tpPct"] = helpers.ClampPct(float64(user.TP), float64(user.MaxTP), 0, 100) data["tppct"] = helpers.ClampPct(float64(user.TP), float64(user.MaxTP), 0, 100)
data["hpColor"] = "" data["hpcolor"] = ""
if hpPct < 35 { if hpPct < 35 {
data["hpColor"] = "danger" data["hpcolor"] = "danger"
} else if hpPct < 75 { } else if hpPct < 75 {
data["hpColor"] = "warning" data["hpcolor"] = "warning"
} }
// Build known healing spells list // Build known healing spells list

View File

@ -140,8 +140,8 @@ func (t *Template) RenderNamed(data map[string]any) string {
result = t.processLoops(result, data) result = t.processLoops(result, data)
result = t.processConditionals(result, data) result = t.processConditionals(result, data)
// Process yield before variable substitution // Process yield with conditionals in blocks
result = t.processYield(result, blocks) result = t.processYield(result, blocks, data)
// Apply data substitutions // Apply data substitutions
for key, value := range data { for key, value := range data {
@ -273,6 +273,20 @@ func (t *Template) getStructField(obj any, fieldName string) any {
return field.Interface() return field.Interface()
} }
func (t *Template) getLength(value any) int {
if value == nil {
return 0
}
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array, reflect.Map, reflect.String:
return rv.Len()
default:
return 0
}
}
func (t *Template) WriteTo(ctx *fasthttp.RequestCtx, data any) { func (t *Template) WriteTo(ctx *fasthttp.RequestCtx, data any) {
var result string var result string
@ -371,12 +385,16 @@ func (t *Template) processBlocks(content string, blocks map[string]string) strin
} }
// processYield handles {yield} directives for template inheritance // processYield handles {yield} directives for template inheritance
func (t *Template) processYield(content string, blocks map[string]string) string { func (t *Template) processYield(content string, blocks map[string]string, data map[string]any) string {
result := content result := content
for blockName, blockContent := range blocks { for blockName, blockContent := range blocks {
// Process conditionals and loops in block content before yielding
processedBlock := t.processLoops(blockContent, data)
processedBlock = t.processConditionals(processedBlock, data)
yieldPlaceholder := fmt.Sprintf("{yield \"%s\"}", blockName) yieldPlaceholder := fmt.Sprintf("{yield \"%s\"}", blockName)
result = strings.ReplaceAll(result, yieldPlaceholder, blockContent) result = strings.ReplaceAll(result, yieldPlaceholder, processedBlock)
} }
// Replace any remaining {yield} with empty string // Replace any remaining {yield} with empty string
@ -714,20 +732,6 @@ func (t *Template) isTruthy(value any) bool {
} }
} }
func (t *Template) getLength(value any) int {
if value == nil {
return 0
}
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array, reflect.Map, reflect.String:
return rv.Len()
default:
return 0
}
}
// RenderToContext is a simplified helper that renders a template and writes it to the request context // RenderToContext is a simplified helper that renders a template and writes it to the request context
// with error handling. Returns true if successful, false if an error occurred (error is written to response). // with error handling. Returns true if successful, false if an error occurred (error is written to response).
func RenderToContext(ctx *fasthttp.RequestCtx, templateName string, data map[string]any) bool { func RenderToContext(ctx *fasthttp.RequestCtx, templateName string, data map[string]any) bool {

View File

@ -1,3 +1,19 @@
<div class="town inn"> {include "layout.html"}
{block "content"}
<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>
<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>
<a href="/town"><button class="btn">No</button></a>
</form>
{/if}
</div> </div>
{/block}

View File

@ -2,7 +2,7 @@
{block "content"} {block "content"}
<div class="town"> <div class="town">
<div class="options"> <section class="options">
<div class="title"><img src="/assets/images/town_{town.ID}.gif" alt="Welcome to {town.Name}" title="Welcome to {town.Name}"></div> <div class="title"><img src="/assets/images/town_{town.ID}.gif" alt="Welcome to {town.Name}" title="Welcome to {town.Name}"></div>
<b>Town Options</b><br> <b>Town Options</b><br>
<ul class="unstyled"> <ul class="unstyled">
@ -10,21 +10,21 @@
<li><a href="/town/shop">Browse the Shop</a></li> <li><a href="/town/shop">Browse the Shop</a></li>
<li><a href="/town/maps">Buy Maps</a></li> <li><a href="/town/maps">Buy Maps</a></li>
</ul> </ul>
</div> </section>
<div class="news"> <section class="news">
{newscontent} {newscontent}
</div> </section>
<div class="split"> <section class="split">
<div class="whos-online"> <section class="whos-online">
{whosonline} {whosonline}
</div> </section>
<div class="babblebox"> <section class="babblebox">
<div class="title">Babblebox</div> <div class="title">Babblebox</div>
@TODO @TODO
</div> </section>
</div> </section>
</div> </div>
{/block} {/block}