From c226f6200fe6b405a935ad5e5d2c62d3c272ad8b Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Mon, 25 Aug 2025 10:50:05 -0500 Subject: [PATCH] start help pages; fixes broken help links --- assets/dk.css | 4 + data/dk.db | Bin 73728 -> 73728 bytes internal/routes/forum.go | 60 +++--------- internal/routes/help.go | 16 ++++ main.go | 1 + templates/help/guide.html | 190 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 226 insertions(+), 45 deletions(-) create mode 100644 internal/routes/help.go create mode 100644 templates/help/guide.html diff --git a/assets/dk.css b/assets/dk.css index 91fc988..56391bb 100644 --- a/assets/dk.css +++ b/assets/dk.css @@ -576,4 +576,8 @@ div.bottom-reply { .mt-1 { margin-top: 1rem; +} + +hr { + margin: 1rem 0; } \ No newline at end of file diff --git a/data/dk.db b/data/dk.db index 727455e604ceee3bfc2cf36c1146e7221b854b28..fda1a74e9e0d885a79853922352e8f6a4701710f 100644 GIT binary patch delta 108 zcmZoTz|wGlWkZcVqvGUR{dQ(WuBgfE298WzQ5zctxb$@uIT=`37&$rQ4Kr3BX98iS zpj0b`lGNgo%F6swg~Xy%g+zt4{CtI^qSCU>omNj+n2$5bY%Z{0Q_sBm*!Riu`*$&& LTe$i5e$@m35=ka1 delta 57 zcmV-90LK4-zyyH61h9NB0U(ooFNXsl3W<{kFhQ|_K?<{pI^+Zcg!3D-a6hbn1E|=! PlOMmj0>)Rf+rK4Xct{uF diff --git a/internal/routes/forum.go b/internal/routes/forum.go index 0ab1d86..946374a 100644 --- a/internal/routes/forum.go +++ b/internal/routes/forum.go @@ -3,6 +3,7 @@ package routes import ( "dk/internal/components" "dk/internal/database" + "dk/internal/helpers" "dk/internal/helpers/markdown" "dk/internal/models/forum" "dk/internal/models/users" @@ -15,8 +16,7 @@ import ( "git.sharkk.net/Sharkk/Sushi/auth" ) -// ThreadData flattened struct for template use -type ThreadData struct { +type threadData struct { ID int Posted int64 LastPost int64 @@ -34,8 +34,7 @@ type ThreadData struct { HasReplies bool } -// ReplyData flattened struct for template use -type ReplyData struct { +type replyData struct { ID int Posted int64 Author int @@ -46,47 +45,18 @@ type ReplyData struct { AuthorClass string } -// Helper methods for ThreadData -func (t *ThreadData) PostedTime() time.Time { +func (t *threadData) PostedTime() time.Time { return time.Unix(t.Posted, 0) } -func (t *ThreadData) LastPostTime() time.Time { +func (t *threadData) LastPostTime() time.Time { return time.Unix(t.LastPost, 0) } -// Helper methods for ReplyData -func (r *ReplyData) PostedTime() time.Time { +func (r *replyData) PostedTime() time.Time { return time.Unix(r.Posted, 0) } -// PaginationParams handles common pagination logic -type PaginationParams struct { - Page int - PerPage int - Total int -} - -func (p PaginationParams) Offset() int { - return (p.Page - 1) * p.PerPage -} - -func (p PaginationParams) TotalPages() int { - pages := (p.Total + p.PerPage - 1) / p.PerPage - if pages < 1 { - return 1 - } - return pages -} - -func (p PaginationParams) HasNext() bool { - return p.Page < p.TotalPages() -} - -func (p PaginationParams) HasPrev() bool { - return p.Page > 1 -} - func RegisterForumRoutes(app *sushi.App) { authed := app.Group("/forum") authed.Use(auth.RequireAuth()) @@ -117,19 +87,19 @@ func validateThread(ctx sushi.Ctx, id int) (*forum.Forum, bool) { } // Helper function to get pagination params from request -func getPaginationParams(ctx sushi.Ctx, perPage int) PaginationParams { +func getPagination(ctx sushi.Ctx, perPage int) helpers.Pagination { page := max(int(ctx.QueryArgs().GetUintOrZero("page")), 1) - return PaginationParams{ + return helpers.Pagination{ Page: page, PerPage: perPage, } } func index(ctx sushi.Ctx) { - pagination := getPaginationParams(ctx, 30) + pagination := getPagination(ctx, 30) // Get threads with user data - var threads []*ThreadData + var threads []*threadData query := ` SELECT f.id, f.posted, f.last_post, f.author, f.parent, f.replies, f.title, f.content, COALESCE(u.username, '[Deleted]') as author_username, @@ -145,7 +115,7 @@ func index(ctx sushi.Ctx) { err := database.Select(&threads, query, pagination.PerPage, pagination.Offset()) if err != nil { log.Printf("Error fetching forum threads: %v", err) - threads = make([]*ThreadData, 0) + threads = make([]*threadData, 0) } // Get last reply info for each thread @@ -235,11 +205,11 @@ func showThread(ctx sushi.Ctx) { return } - pagination := getPaginationParams(ctx, 10) + pagination := getPagination(ctx, 10) pagination.Total = thread.Replies // Get thread with author data - var threadData ThreadData + var threadData threadData err := database.Get(&threadData, ` SELECT f.id, f.posted, f.last_post, f.author, f.parent, f.replies, f.title, f.content, COALESCE(u.username, '[Deleted]') as author_username, @@ -259,7 +229,7 @@ func showThread(ctx sushi.Ctx) { } // Get replies with user data - var replies []*ReplyData + var replies []*replyData err = database.Select(&replies, ` SELECT f.id, f.posted, f.author, f.title, f.content, COALESCE(u.username, '[Deleted]') as author_username, @@ -274,7 +244,7 @@ func showThread(ctx sushi.Ctx) { if err != nil { log.Printf("Error loading replies for thread %d: %v", id, err) - replies = make([]*ReplyData, 0) + replies = make([]*replyData, 0) } components.RenderPage(ctx, threadData.Title, "forum/thread.html", map[string]any{ diff --git a/internal/routes/help.go b/internal/routes/help.go new file mode 100644 index 0000000..65db1b8 --- /dev/null +++ b/internal/routes/help.go @@ -0,0 +1,16 @@ +package routes + +import ( + "dk/internal/components" + + sushi "git.sharkk.net/Sharkk/Sushi" +) + +func RegisterHelpRoutes(app *sushi.App) { + group := app.Group("/help") + group.Get("/", guide) +} + +func guide(ctx sushi.Ctx) { + components.RenderPage(ctx, "Help", "help/guide.html", map[string]any{}) +} diff --git a/main.go b/main.go index 2e8785f..9cdc5ca 100644 --- a/main.go +++ b/main.go @@ -180,6 +180,7 @@ func start(port string) error { routes.RegisterTownRoutes(app) routes.RegisterFightRoutes(app) routes.RegisterForumRoutes(app) + routes.RegisterHelpRoutes(app) app.Get("/assets/*path", sushi.Static(cwd)) diff --git a/templates/help/guide.html b/templates/help/guide.html new file mode 100644 index 0000000..676a122 --- /dev/null +++ b/templates/help/guide.html @@ -0,0 +1,190 @@ +{include "layout.html"} + +{block "content"} + +

Help

+[ Return to the game ] + +
+ +

Table of Contents

+ + +
+ +

Introduction

+Firstly, I'd like to say thank you for playing my game. The Dragon Knight game engine is the result of several months of +planning, coding and testing. The original idea was to create a web-based tribute to the NES game, Dragon +Warrior. In its current iteration, only the underlying fighting system really resembles that game, as almost +everything else in DK has been made bigger and better. But you should still recognize bits and pieces as stemming +from Dragon Warrior and other RPGs of old.

+This is the first game I've ever written, and it has definitely been a positive experience. It got difficult at +times, admittedly, but it was still a lot of fun to write, and even more fun to play. And I hope to use this +experience so that if I ever want to create another game it will be even better than this one.

+If you are a site administrator, and would like to install a copy of DK on your own server, you may visit the +development site for Dragon Knight. This page +includes the downloadable game souce code, as well as some other resources that developers and administrators may +find valuable.

+Once again, thanks for playing!

+Jamin Seven
+Dragon Knight creator
+My Homepage
+Dragon Knight Homepage

+[ Top ] + +
+ +

Character Classes

+There are three character classes in the game. The main differences between the classes are what spells you get +access to, the speed with which you level up, and the amount of HP/MP/strength/dexterity you gain per level. Below +is a basic outline of each of the character classes. For more detailed information about the characters, please +view the Levels table at the bottom of this page. Also, note that the outline below refers to the stock class setup +for the game. If your administrator has used his/her own class setup, this information may not be accurate.

+@TODO + +
+ +

In Town

+When you begin a new game, the first thing you see is the Town screen. Towns serve four primary functions: healing, buying items, +buying maps, and displaying game information.

+To heal yourself, click the "Rest at the Inn" link at the top of the town screen. Each town's Inn has a different price - some towns +are cheap, others are expensive. No matter what town you're in, the Inns always serve the same function: they restore your current +hit points, magic points, and travel points to their maximum amounts. Out in the field, you are free to use healing spells to restore +your hit points, but when you run low on magic points, the only way to restore them is at an Inn.

+Buying weapons and armor is accomplished through the appropriately-named "Buy Weapons/Armor" link. Not every item is available in +every town, so in order to get the most powerful items, you'll need to explore some of the outer towns. Once you've clicked the link, +you are presented with a list of items available in this town's store. To the left of each item is an icon that represents its type: +weapon, armor or shield. The amount of attack/defense power, as well as the item's price, are displayed to the right of the item name. +You'll notice that some items have a red asterisk (*) next to their names. These are items that come +with special attributes that modify other parts of your character profile. See the Items & Drops table at the bottom of this page for +more information about special items.

+Maps are the third function in towns. Buying a map to a town places the town in your Travel To box in the left status panel. Once +you've purchased a town's map, you can click its name from your Travel To box and you will jump to that town. Travelling this way +costs travel points, though, and you'll only be able to visit towns if you have enough travel points.

+The final function in towns is displaying game information and statistics. This includes the latest news post made by the game +administrator, a list of players who have been online recently, and the Babble Box.

+[ Top ] + +
+ +

Exploring & Fighting

+Once you're done in town, you are free to start exploring the world. Use the compass buttons on the left status panel to move around. +The game world is basically a big square, divided into four quadrants. Each quadrant is {control.WorldSize} spaces +square. The first town is usually located at (0N,0E). Click the North button from the first town, and now you'll be at (1N,0E). +Likewise, if you now click the West button, you'll be at (1N,1W). Monster levels increase with every 5 spaces you move outward +from (0N,0E).

+While you're exploring, you will occasionally run into monsters. As in pretty much any other RPG game, you and the monster take turns +hitting each other in an attempt to reduce each other's hit points to zero. Once you run into a monster, the Exploring screen changes +to the Fighting screen.

+When a fight begins, you'll see the monster's name and hit points, and the game will ask you for your first command. You then get to +pick whether you want to fight, use a spell, or run away. Note, though, that sometimes the monster has the chance to hit you +first.

+The Fight button is pretty straightforward: you attack the monster, and the amount of damage dealt is based on your attack power and +the monster's armor. On top of that, there are two other things that can happen: an Excellent Hit, which doubles your total attack +damage; and a monster dodge, which results in you doing no damage to the monster.

+The Spell button allows you to pick an available spell and cast it. See the Spells list at the bottom of this page for more information +about spells.

+Finally, there is the Run button, which lets you run away from a fight if the monster is too powerful. Be warned, though: it is +possible for the monster to block you from running and attack you. So if your hit points are low, you may fare better by staying +around monsters that you know can't do much damage to you.

+Once you've had your turn, the monster also gets his turn. It is also possible for you to dodge the monster's attack and take no +damage.

+The end result of a fight is either you or the monster being knocked down to zero hit points. If you win, the monster dies and will +give you a certain amount of experience and gold. There is also a chance that the monster will drop an item, which you can put into +one of the three inventory slots to give you extra points in your character profile. If you lose and die, half of your gold is taken +away - however, you are given back a few hit points to help you make it back to town (for example, if you don't have enough gold to +pay for an Inn, and need to kill a couple low-level monsters to get the money).

+When the fight is over, you can continue exploring until you find another monster to beat into submission.

+[ Top ] + +
+ +

Status Panels

+There are two status panels on the game screen: left and right.

+The left panel inclues your current location and play status (In Town, Exploring, Fighting), compass buttons for movement, and the +Travel To list for jumping between towns. At the bottom of the left panel is also a list of game functions.

+The right panel displays some character statistics, your inventory, and quick spells.

+The Character section shows the most important character statistics. It also displays the status bars for your current hit points, +magic points and travel points. These status bars are colored either green, yellow or red depending on your current amount of each +stat. There is also a link to pop up your list of extended statistics, which shows more detailed character information.

+The Fast Spells section lists any Heal spells you've learned. You may use these links any time you are in town or exploring to cast +the heal spell. These may not be used during fights, however - you have to use the Spells box on the fight screen for that. +[ Top ] + +
+ +

Items & Drops

+Click here for the Items & Drops spoiler page.

+[ Top ] + +
+ +

Monsters

+Click here for the Monsters spoiler page.

+[ Top ] + +
+ +

Spells

+Click here for the Spells spoiler page.

+[ Top ] + +
+ +

Credits

+ +Apologies and lots of happy naked love to anyone I forgot.

+And of course, thanks to you for playing my game!

+NINJA!

+[ Top ] + +
+ +Please visit the following sites for more information:
+Se7enet (Jamin's homepage)
+Dragon Knight (official DK homepage)
+Forums (official DK forums)

+All original coding and graphics for the Dragon Knight game engine are © 2003-2005 by Jamin Seven.

+[ Top ] +{/block}