So many changes... sheesh. Adds HTMX, reworks rendering, etc

This commit is contained in:
Sky Johnson 2024-12-18 10:00:41 -06:00
parent 169e617989
commit 1b9106fadb
18 changed files with 316 additions and 210 deletions

View File

@ -96,6 +96,7 @@ a {
color: #663300;
text-decoration: none;
font-weight: bold;
cursor: pointer;
}
a:hover {

View File

@ -8,7 +8,7 @@ html {
}
body {
background-image: url('/img/background.jpg');
background-image: url('/img/backgrounds/background.jpg');
padding: 2rem;
}
table {

View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -8,7 +8,7 @@ $r = new Router;
$r->get('/', function() {
if (user()->currentaction === "In Town") {
$page = dotown();
$page = Towns\town();
$title = "In Town";
} elseif (user()->currentaction === "Exploring") {
$page = doexplore();
@ -17,7 +17,7 @@ $r->get('/', function() {
redirect('/fight');
}
display($page, $title);
return is_htmx() ? $page : display($page, $title);
});
$r->get('/ninja', function() {
@ -45,83 +45,15 @@ $l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
if (is_int($l)) exit("Error: $l");
if (!empty($l['middleware'])) foreach ($l['middleware'] as $middleware) $middleware();
$l['handler'](...$l['params'] ?? []);
function donothing()
{
if (user()->currentaction == "In Town") {
$page = dotown();
$title = "In Town";
} elseif (user()->currentaction == "Exploring") {
$page = doexplore();
$title = "Exploring";
} elseif (user()->currentaction == "Fighting") {
redirect('/fight');
}
display($page, $title);
}
/**
* Spit out the main town page.
*/
function dotown()
{
global $controlrow;
$townrow = get_town_by_xy(user()->longitude, user()->latitude);
if ($townrow === false) display("There is an error with your user account, or with the town data. Please try again.","Error");
$townrow["news"] = "";
$townrow["whosonline"] = "";
$townrow["babblebox"] = "";
// News box. Grab latest news entry and display it. Something a little more graceful coming soon maybe.
if ($controlrow["shownews"] == 1) {
$newsrow = db()->query('SELECT * FROM news ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC);
$townrow["news"] = '<div class="title">Latest News</div>';
$townrow["news"] .= "<span class=\"light\">[".pretty_date($newsrow["postdate"])."]</span><br>".nl2br($newsrow["content"]);
}
// Who's Online. Currently just members. Guests maybe later.
if ($controlrow["showonline"] == 1) {
$onlinequery = db()->query(<<<SQL
SELECT id, username
FROM users
WHERE onlinetime >= datetime('now', '-600 seconds')
ORDER BY username;
SQL);
$online_count = 0;
$online_rows = [];
while ($onlinerow = $onlinequery->fetchArray(SQLITE3_ASSOC)) {
$online_count++;
$online_rows[] = "<a href=\"javascript:opencharpopup({$onlinerow['id']})\">".$onlinerow["username"]."</a>";
}
$townrow["whosonline"] = '<div class="title">Who\'s Online</div>';
$townrow["whosonline"] .= "There are <b>$online_count</b> user(s) online within the last 10 minutes: ";
$townrow["whosonline"] .= rtrim(implode(', ', $online_rows), ', ');
}
if ($controlrow["showbabble"] == 1) {
$townrow["babblebox"] = <<<HTML
<div class="title">Babble Box</div>
<iframe src="/babblebox" name="sbox" width="100%" height="250" frameborder="0" id="bbox">
Your browser does not support inline frames! The Babble Box will not be available until you upgrade to
a newer <a href="http://www.mozilla.org" target="_new">browser</a>.
</iframe>
HTML;
}
$u = User::find(1);
$u->gold += 100;
$u->save();
var_dump($u->gold);
return render('towns', ['town' => $townrow]);
$content = $l['handler'](...$l['params'] ?? []);
if (is_htmx()) {
$content .= Render\debug_db_info();
if ($GLOBALS['state']['user-state-changed'] ?? false) {
$content .= Render\right_nav();
}
}
echo $content;
exit;
/**
* Just spit out a blank exploring page. Exploring without a GET string is normally when they first log in, or when
@ -130,12 +62,8 @@ function dotown()
function doexplore()
{
return <<<HTML
<table width="100%">
<tr><td class="title"><img src="/img/title_exploring.gif" alt="Exploring" /></td></tr>
<tr><td>
You are exploring the map, and nothing has happened. Continue exploring using the direction buttons or the Travel To menus.
</td></tr>
</table>
<div class="title"><img src="/img/title_exploring.gif" alt="Exploring"></div>
You are exploring the map, and nothing has happened. Continue exploring using the direction buttons or the Travel To menus.
HTML;
}
@ -167,7 +95,7 @@ function show_character_info(int $id = 0): void
'magic_list' => $magic_list,
'controlrow' => $controlrow
]);
echo render('minimal', ['content' => $showchar, 'title' => $userrow['username'].' Information']);
echo render('layouts/minimal', ['content' => $showchar, 'title' => $userrow['username'].' Information']);
}
function showmap()
@ -178,7 +106,7 @@ function showmap()
round(258 - user()->latitude * (500 / 500) - 3)
);
echo render('minimal', [
echo render('layouts/minimal', [
'content' => '<img src="/img/map.gif" alt="Map">'.$pos,
'title' => 'Map'
]);

1
public/js/htmx.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -495,7 +495,7 @@ function levels()
function display_help(string $content)
{
global $controlrow;
echo render('help', [
echo render('layouts/help', [
'control' => $controlrow,
'content' => $content,
'version' => VERSION,

View File

@ -20,40 +20,99 @@ function register_routes(Router $r): Router
return $r;
}
/**
* Spit out the main town page.
*/
function town()
{
global $controlrow;
$townrow = get_town_by_xy(user()->longitude, user()->latitude);
if ($townrow === false) display("There is an error with your user account, or with the town data. Please try again.","Error");
$townrow["news"] = "";
$townrow["whosonline"] = "";
$townrow["babblebox"] = "";
// News box. Grab latest news entry and display it. Something a little more graceful coming soon maybe.
if ($controlrow["shownews"] == 1) {
$newsrow = db()->query('SELECT * FROM news ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC);
$townrow["news"] = '<div class="title">Latest News</div>';
$townrow["news"] .= "<span class=\"light\">[".pretty_date($newsrow["postdate"])."]</span><br>".nl2br($newsrow["content"]);
}
// Who's Online. Currently just members. Guests maybe later.
if ($controlrow["showonline"] == 1) {
$onlinequery = db()->query(<<<SQL
SELECT id, username
FROM users
WHERE onlinetime >= datetime('now', '-600 seconds')
ORDER BY username;
SQL);
$online_count = 0;
$online_rows = [];
while ($onlinerow = $onlinequery->fetchArray(SQLITE3_ASSOC)) {
$online_count++;
$online_rows[] = "<a href=\"javascript:opencharpopup({$onlinerow['id']})\">".$onlinerow["username"]."</a>";
}
$townrow["whosonline"] = '<div class="title">Who\'s Online</div>';
$townrow["whosonline"] .= "There are <b>$online_count</b> user(s) online within the last 10 minutes: ";
$townrow["whosonline"] .= rtrim(implode(', ', $online_rows), ', ');
}
if ($controlrow["showbabble"] == 1) {
$townrow["babblebox"] = <<<HTML
<div class="title">Babble Box</div>
<iframe src="/babblebox" name="sbox" width="100%" height="250" frameborder="0" id="bbox">
Your browser does not support inline frames! The Babble Box will not be available until you upgrade to
a newer <a href="http://www.mozilla.org" target="_new">browser</a>.
</iframe>
HTML;
}
if (is_htmx()) htmx_update_page_title($townrow['name']);
return render('towns', ['town' => $townrow]);
}
/**
* Staying at the inn resets all expendable stats to their max values.
*/
function inn()
{
$townrow = get_town_by_xy(user()->longitude, user()->latitude);
if ($townrow === false) { display("Cheat attempt detected.<br><br>Get a life, loser.", "Error"); }
$town = get_town_by_xy(user()->longitude, user()->latitude);
if ($town === false) { exit('Cheat attempt detected.<br><br>Get a life, loser.'); }
if (user()->gold < $townrow["innprice"]) {
display("You do not have enough gold to stay at this Inn tonight.<br><br>You may return to <a href=\"/\">town</a>, or use the direction buttons on the left to start exploring.", "Inn");
}
$htmx = is_htmx();
if ($htmx) htmx_update_page_title($town['name'] . ' Inn');
if (isset($_POST["submit"])) {
$newgold = user()->gold - $townrow["innprice"];
db()->query(
'UPDATE users SET gold=?, currenthp=?, currentmp=?, currenttp=? WHERE id=?',
[$newgold, user()->maxhp, user()->maxmp, user()->maxtp, user()->id
]);
$title = "Inn";
$page = "You wake up feeling refreshed and ready for action.<br><br>You may return to <a href=\"/\">town</a>, or use the direction buttons on the left to start exploring.";
} elseif (isset($_POST["cancel"])) {
if (user()->gold < $town['innprice']) {
$page = <<<HTML
You do not have enough gold to stay at this Inn tonight. <br><br>
You may return to <a hx-get="/" hx-target="#middle">town</a>, or use the direction buttons on the left to start exploring.
HTML;
} elseif (isset($_POST['submit'])) {
user()->gold -= $town['innprice'];
user()->restore_points()->save();
$page = <<<HTML
You wake up feeling refreshed and ready for action. <br><br>
You may return to <a hx-get="/" hx-target="#middle">town</a>, or use the direction buttons on the left to start exploring.
HTML;
} elseif (isset($_POST['cancel'])) {
redirect('/');
} else {
$title = "Inn";
$page = <<<HTML
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>{$townrow["innprice"]} gold</b>. Is that ok?<br><br>
<form action="/inn" method="post">
<input type="submit" name="submit" value="Yes"> <input type="submit" name="cancel" value="No">
</form>
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['innprice']} gold</b>. Is that ok?<br><br>
<form hx-post="/inn" hx-target="#middle">
<input type="submit" name="submit" value="Yes"> <input type="submit" name="cancel" value="No">
</form>
HTML;
}
display($page, $title);
return $htmx ? $page : display($page, $town['name'] . ' Inn');
}
/**
@ -61,32 +120,51 @@ function inn()
*/
function buy()
{
$townrow = get_town_by_xy(user()->longitude, user()->latitude);
if ($townrow === false) display("Cheat attempt detected.<br><br>Get a life, loser.", "Error");
$town = get_town_by_xy(user()->longitude, user()->latitude);
if ($town === false) { exit('Cheat attempt detected.<br><br>Get a life, loser.'); }
$items = db()->query("SELECT * FROM items WHERE id IN ({$townrow["itemslist"]});");
$page = "Buying weapons will increase your Attack Power. Buying armor and shields will increase your Defense Power.<br><br>Click an item name to purchase it.<br><br>The following items are available at this town:<br><br>\n";
$page .= "<table width=\"80%\">\n";
while ($itemsrow = $items->fetchArray(SQLITE3_ASSOC)) {
$attrib = ($itemsrow["type"] == 1) ? "Attack Power:" : "Defense Power:";
$page .= "<tr><td width=\"4%\">";
$page .= match ($itemsrow["type"]) {
1 => '<img src="/img/icon_weapon.gif" alt="weapon" /></td>',
2 => '<img src="/img/icon_armor.gif" alt="armor" /></td>',
3 => '<img src="/img/icon_shield.gif" alt="shield" /></td>'
$htmx = is_htmx();
if ($htmx) htmx_update_page_title($town['name'] . ' Shop');
$page = <<<HTML
Buying weapons will increase your Attack Power. Buying armor and shields will increase your Defense Power.<br><br>
Click an item name to purchase it.<br><br>
The following items are available at this town:<br><br>
<table>
HTML;
$items = db()->query('SELECT * FROM items WHERE id IN (' . $town["itemslist"] . ');');
while ($item = $items->fetchArray(SQLITE3_ASSOC)) {
$attrib = ($item["type"] == 1) ? "Attack Power:" : "Defense Power:";
$page .= '<tr><td width="4%">';
$page .= match ($item["type"]) {
1 => '<img src="/img/icon_weapon.gif" alt="weapon">',
2 => '<img src="/img/icon_armor.gif" alt="armor">',
3 => '<img src="/img/icon_shield.gif" alt="shield">'
};
if (user()->weaponid == $itemsrow["id"] || user()->armorid == $itemsrow["id"] || user()->shieldid == $itemsrow["id"]) {
$page .= "<td width=\"32%\"><span class=\"light\">".$itemsrow["name"]."</span></td><td width=\"32%\"><span class=\"light\">$attrib ".$itemsrow["attribute"]."</span></td><td width=\"32%\"><span class=\"light\">Already purchased</span></td></tr>\n";
$page .= '</td>';
if (user()->weaponid === $item["id"] || user()->armorid === $item["id"] || user()->shieldid === $item["id"]) {
$page .= <<<HTML
<td width="32%"><span class="light">{$item["name"]}</span></td>
<td width="32%"><span class="light">$attrib {$item['attribute']}</span></td>
<td width="32%"><span class="light">Already purchased</span></td>
HTML;
} else {
if ($itemsrow["special"] != "X") { $specialdot = "<span class=\"highlight\">&#42;</span>"; } else { $specialdot = ""; }
$page .= "<td width=\"32%\"><b><a href=\"/buy2/{$itemsrow["id"]}\">".$itemsrow["name"]."</a>$specialdot</b></td><td width=\"32%\">$attrib <b>".$itemsrow["attribute"]."</b></td><td width=\"32%\">Price: <b>".$itemsrow["buycost"]." gold</b></td></tr>\n";
$specialdot = $item['special'] !== 'X' ? '<span class="highlight">&#42;</span>' : '';
$page .= <<<HTML
<td width="32%"><b><a href="/buy2/{$item['id']}">{$item['name']}</a>$specialdot</b></td>
<td width="32%">$attrib <b>{$item['attribute']}</b></td>
<td width="32%">Price: <b>{$item['buycost']} gold</b></td>
HTML;
}
$page .= '</tr>';
}
$page .= "</table><br>\n";
$page .= "If you've changed your mind, you may also return back to <a href=\"/\">town</a>.\n";
$title = "Buy Items";
$page .= <<<HTML
</table><br>
If you've changed your mind, you may also return back to <a hx-get="/" hx-target="#middle">town</a>.
HTML;
display($page, $title);
return $htmx ? $page : display($page, $town['name'] . ' Shop');
}
/**

View File

@ -4,6 +4,7 @@ require_once 'lib.php';
require_once 'router.php';
require_once 'auth.php';
require_once 'mail.php';
require_once 'render.php';
require_once 'actions/explore.php';
require_once 'actions/heal.php';
require_once 'actions/users.php';

View File

@ -19,7 +19,13 @@ function db(): Database
*/
function redirect(string $location): void
{
header("Location: $location");
if (is_htmx()) {
header("HX-Redirect: $location");
header("HX-Replace-Url: $location");
} else {
header("Location: $location");
}
exit;
}
@ -82,7 +88,7 @@ function make_safe(string $content): string
*/
function display_admin($content, $title)
{
echo render('admin', [
echo render('layouts/admin', [
"title" => $title,
"content" => $content,
"totaltime" => round(microtime(true) - START, 4),
@ -97,28 +103,17 @@ function display_admin($content, $title)
/**
* Finalize page and output to browser.
*/
function display($content, $title, bool $topnav = true, bool $leftnav = true, bool $rightnav = true): void
function display($content, $title, bool $topnav = true, bool $leftnav = true, bool $rightnav = true): string
{
global $controlrow;
if ($topnav == true) {
if (user() !== false) { // user should be logged in
$topnav = <<<HTML
<a href='/logout'><img src='/img/button_logout.gif' alt='Log Out' title='Log Out'></a>
<a href='/help'><img src='/img/button_help.gif' alt='Help' title='Help'></a>
HTML;
} else {
$topnav = <<<HTML
<a href='/login'><img src='/img/button_login.gif' alt='Log In' title='Log In'></a>
<a href='/register'><img src='/img/button_register.gif' alt='Register' title='Register'></a>
<a href='/help'><img src='/img/button_help.gif' alt='Help' title='Help'></a>
HTML;
}
} else {
$topnav = '';
}
$game_skin = 0;
$topnav = $topnav ? Render\header_links() : '';
if (user() !== false) {
$game_skin = user()->game_skin;
if (user()->currentaction == 'In Town') {
$town = get_town_by_xy(user()->latitude, user()->longitude);
$current_town = "Welcome to <b>{$town['name']}</b>.<br><br>";
@ -129,26 +124,6 @@ function display($content, $title, bool $topnav = true, bool $leftnav = true, bo
// Format various userrow stuffs...
if (user()->latitude < 0) { user()->latitude = user()->latitude * -1 . "S"; } else { user()->latitude .= "N"; }
if (user()->longitude < 0) { user()->longitude = user()->longitude * -1 . "W"; } else { user()->longitude .= "E"; }
user()->experience = number_format(user()->experience);
user()->gold = number_format(user()->gold);
// Now make numbers stand out if they're low.
if (user()->currenthp <= (user()->maxhp/5)) { user()->currenthp = "<blink><span class=\"highlight\"><b>*".user()->currenthp."*</b></span></blink>"; }
if (user()->currentmp <= (user()->maxmp/5)) { user()->currentmp = "<blink><span class=\"highlight\"><b>*".user()->currentmp."*</b></span></blink>"; }
$user_spells = explode(',', user()->spells);
$spellquery = get_spells_from_list($user_spells);
user()->magiclist = '';
while ($spell = $spellquery->fetchArray(SQLITE3_ASSOC)) {
$spell = false;
foreach($user_spells as $id) {
if ($id === $spell['id'] && $spell['type'] == 1) $spell = true;
}
if ($spell == true) {
user()->magiclist .= "<a href=\"/spell/{$spell['id']}\">".$spell['name']."</a><br>";
}
}
if (user()->magiclist == "") { user()->magiclist = "None"; }
// Travel To list.
$townslist = explode(",",user()->towns);
@ -165,22 +140,14 @@ function display($content, $title, bool $topnav = true, bool $leftnav = true, bo
}
}
echo render('primary', [
return render('layouts/primary', [
"dkgamename" => $controlrow["gamename"],
"title" => $title,
"content" => $content,
"game_skin" => user()->game_skin ??= '0',
'rightnav' => $rightnav ? render('rightnav', ['statbars' => create_stat_table(user())]) : '',
"game_skin" => $game_skin,
"leftnav" => $leftnav ? render('leftnav', ['town_list' => $town_list_html, 'current_town' => $current_town]) : '',
"topnav" => $topnav,
"totaltime" => round(microtime(true) - START, 4),
"numqueries" => db()->count,
"version" => VERSION,
"build" => BUILD,
"querylog" => env('debug', false) ? db()->log : []
]);
exit;
}
function checkcookies()
@ -575,32 +542,36 @@ function env(string $key, mixed $default = null): mixed
/**
* Get the data on spells from a given list of IDs.
*/
function get_spells_from_list(array|string $spell_ids): SQLite3Result|false
function get_spells_from_list(array|string $spell_ids): array|false
{
if (is_string($spell_ids)) $spell_ids = explode(',', $spell_ids);
$placeholders = implode(',', array_fill(0, count($spell_ids), '?'));
$query = db()->query("SELECT id, name, type FROM spells WHERE id IN($placeholders)", $spell_ids);
if ($query === false) return false;
return $query;
$rows = [];
while ($row = $query->fetchArray(SQLITE3_ASSOC)) $rows[] = $row;
return !empty($rows) ? $rows : false;
}
function generate_stat_bar($current, $max)
function generate_stat_bar(int $current, int $max): string
{
$percent = ($max === 0) ? 0 : ceil($current / $max * 100);
$percent = $max > 0 ? round(max(0, $current) / $max * 100, 4) : 0;
$color = $percent >= 66 ? 'green' : ($percent >= 33 ? 'yellow' : 'red');
return '<div class="stat-bar" style="width: 15px; height: 100px; border: solid 1px black;">' .
'<div style="height: ' . $percent . 'px; background-image: url(/img/bars_' . $color . '.gif);"></div>' .
'</div>';
return <<<HTML
<div class="stat-bar" style="width: 15px; height: 100px; border: solid 1px black;">
<div style="height: $percent%; background-image: url(/img/bars_$color.gif);"></div>
</div>
HTML;
}
function create_stat_table($userrow)
function create_stat_table(): string
{
$stat_table = '<div class="stat-table">' .
'<div class="stat-row">' .
'<div class="stat-col">' . generate_stat_bar(user()->currenthp, user()->maxhp) . '<div>HP</div></div>' .
'<div class="stat-col">' . generate_stat_bar(user()->currentmp, user()->maxmp) . '<div>MP</div></div>' .
'<div class="stat-col">' . generate_stat_bar(user()->currenttp, user()->maxtp) . '<div>TP</div></div>' .
'<div class="stat-col">' . generate_stat_bar((int)user()->currenthp, (int)user()->maxhp) . '<div>HP</div></div>' .
'<div class="stat-col">' . generate_stat_bar((int)user()->currentmp, (int)user()->maxmp) . '<div>MP</div></div>' .
'<div class="stat-col">' . generate_stat_bar((int)user()->currenttp, (int)user()->maxtp) . '<div>TP</div></div>' .
'</div>' .
'</div>';
@ -612,6 +583,23 @@ function create_stat_table($userrow)
*/
function user(): User|false
{
$GLOBALS['state']['user'] ??= ($_SESSION['user_id'] ? User::find($_SESSION['user_id']) : false);
$GLOBALS['state']['user'] ??= (isset($_SESSION['user_id']) ? User::find($_SESSION['user_id']) : false);
return $GLOBALS['state']['user'];
}
/**
* Determine whether a request is from HTMX. If HTMX is trying to restore history, we will say no in order to render
* full pages.
*/
function is_htmx(): bool
{
if (isset($_SERVER['HTTP_HX_HISTORY_RESTORE_REQUEST']) && $_SERVER['HTTP_HX_HISTORY_RESTORE_REQUEST'] === 'true') return false;
return isset($_SERVER['HTTP_HX_REQUEST']) && $_SERVER['HTTP_HX_REQUEST'] === 'true';
}
/**
* Update the page title using HTMX.
*/
function htmx_update_page_title(string $new_title) {
header('HX-Trigger: ' . json_encode(['updateTitle' => ['title' => $new_title]]));
}

View File

@ -19,7 +19,11 @@ class Model
public function __set(string $key, mixed $value): void
{
if (array_key_exists($key, $this->original_data)) $this->changes[$key] = $value;
if (array_key_exists($key, $this->original_data)) {
$this->changes[$key] = $value;
} else {
throw new InvalidArgumentException("Attempted to write to $key, which doesn't exist in the data for this model.");
}
}
public function save(): bool

View File

@ -18,4 +18,48 @@ class User extends Model
if ($data === false) return false;
return new User($data);
}
/**
* Return a list of spells from this user's spell list.
*/
public function spells(): array|false
{
return get_spells_from_list($this->spells);
}
/**
* Restore all HP, MP, and TP values to their max.
*/
public function restore_points(): User
{
$this->currenthp = $this->maxhp;
$this->currentmp = $this->maxmp;
$this->currenttp = $this->maxtp;
return $this;
}
/**
* Save works just as it does on the Model class. In our case, though, user state changing may necessitate
* OOB swaps for parts of the UI that have user data displayed. Left and right nav, for example. In these cases,
* we set a flag in GLOBALS state to signify this.
*/
public function save(): bool
{
if (empty($this->changes)) return true;
$placeholders = [];
$values = [];
foreach ($this->changes as $key => $value) {
$placeholders[] = "$key=?";
$values[] = $value;
}
$values[] = $this->id;
$query = 'UPDATE ' . $this->table_name . ' SET ' . implode(', ', $placeholders) . ' WHERE id = ?;';
$result = db()->query($query, $values);
if ($result === false) return false;
$GLOBALS['state']['user-state-changed'] = true;
return true;
}
}

40
src/render.php Normal file
View File

@ -0,0 +1,40 @@
<?php
namespace Render;
/*
This file contains functions to render various UI elements. The goal is to begin shifting elements in the game
to HTMX/AJAX for more fluid gameplay.
*/
function header_links(): string
{
if (user() !== false) {
$links = "<a href='/logout'><img src='/img/button_logout.gif' alt='Log Out' title='Log Out'></a>";
} else {
$links = <<<HTML
<a href='/login'><img src='/img/button_login.gif' alt='Log In' title='Log In'></a>
<a href='/register'><img src='/img/button_register.gif' alt='Register' title='Register'></a>
HTML;
}
return $links .= "<a href='/help'><img src='/img/button_help.gif' alt='Help' title='Help'></a>";
}
function debug_db_info(): string {
$total_time = round(microtime(true) - START, 4);
return '<div id="debug-db-info" hx-swap-oob="true">'. $total_time . ' Seconds, ' . db()->count . ' Queries</div>';
}
function right_nav(): string
{
if (user() === false) return '';
// Flashy numbers if they're low
$hp = (user()->currenthp <= (user()->maxhp / 5)) ? "<blink><span class=\"highlight\"><b>*" . user()->currenthp . "*</b></span></blink>" : user()->currenthp;
$mp = (user()->currentmp <= (user()->maxmp / 5)) ? "<blink><span class=\"highlight\"><b>*" . user()->currentmp . "*</b></span></blink>" : user()->currentmp;
$template = render('right_nav', ['hp' => $hp, 'mp' => $mp]);
if (is_htmx()) $template = '<section id="right" hx-swap-oob="true">'.$template."</section>";
return $template;
}

View File

@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $title ?></title>
<link rel="stylesheet" href="/css/dk.css">
<script src="/js/htmx.js"></script>
<script>
function opencharpopup(id = 0)
@ -29,20 +30,20 @@
<main>
<section id="left"><?= $leftnav ?></section>
<section id="middle"><?= $content ?></section>
<section id="right"><?= $rightnav ?></section>
<section id="right"><?= Render\right_nav() ?></section>
</main>
<footer>
<div>Powered by <a href="/" target="_new">Dragon Knight</a></div>
<div>&copy; 2024 Sharkk</div>
<div><?= $totaltime ?> Seconds, <?= $numqueries ?> Queries</div>
<div>Version <?= $version ?> <?= $build ?></div>
<?= Render\debug_db_info(); ?>
<div>Version <?= VERSION ?> <?= BUILD ?></div>
</footer>
<?php
if (!empty($querylog)) {
if (env('debug', false)) {
echo '<pre>';
foreach ($querylog as $record) {
foreach (db()->log as $record) {
$query_string = str_replace(["\r\n", "\n", "\r"], ' ', $record[0]);
$error_string = !empty($record[2]) ? '// '.$record[2] : '';
echo '<div>['.round($record[1], 2)."s] {$query_string}{$error_string}</div>";
@ -51,5 +52,15 @@
}
?>
</div>
<script>
document.addEventListener("updateTitle", (event) => {
const title = event.detail?.title;
if (title) {
console.log('New title:', title);
document.title = title;
}
});
</script>
</body>
</html>

View File

@ -2,26 +2,36 @@
<div class="title"><img src="/img/button_character.gif" alt="Character" title="Character"></div>
<b><?= user()->username ?></b><br>
Level: <?= user()->level ?><br>
Exp: <?= user()->experience ?><br>
Gold: <?= user()->gold ?><br>
HP: <?= user()->currenthp ?><br>
MP: <?= user()->currentmp ?><br>
Exp: <?= number_format(user()->experience) ?><br>
Gold: <?= number_format(user()->gold) ?><br>
HP: <?= $hp ?><br>
MP: <?= $mp ?><br>
TP: <?= user()->currenttp ?><br><br>
<?= $statbars ?><br>
<?= create_stat_table() ?><br>
<a href="javascript:opencharpopup()">Extended Stats</a>
</section>
<section>
<div class="title"><img src="/img/button_inventory.gif" alt="Inventory" title="Inventory"></div>
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> Weapon: <?= $user['weaponname'] ?><br>
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> Armor: <?= $user['armorname'] ?><br>
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> Shield: <?= $user['shieldname'] ?><br>
Slot 1: <?= $user['slot1name'] ?><br>
Slot 2: <?= $user['slot2name'] ?><br>
Slot 3: <?= $user['slot3name'] ?>
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> Weapon: <?= user()->weaponname ?><br>
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> Armor: <?= user()->armorname ?><br>
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> Shield: <?= user()->shieldname ?><br>
Slot 1: <?= user()->slot1name ?><br>
Slot 2: <?= user()->slot2name ?><br>
Slot 3: <?= user()->slot3name ?>
</section>
<section>
<div class="title"><img src="/img/button_fastspells.gif" alt="Fast Spells" title="Fast Spells"></div>
<?= $user['magiclist'] ?>
<?php
$user_spells = user()->spells();
if ($user_spells !== false) {
foreach ($user_spells as $spell) {
// list only healing spells for now
if ($spell['type'] === 1) echo "<a href=\"/spell/{$spell['id']}\">".$spell['name']."</a><br>";
}
} else {
echo 'None';
}
?>
</section>

View File

@ -3,8 +3,8 @@
<div class="title"><img src="/img/town_<?= $town['id'] ?>.gif" alt="Welcome to <?= $town['name'] ?>" title="Welcome to <?= $town['name'] ?>"></div>
<b>Town Options:</b><br>
<ul>
<li><a href="/inn">Rest at the Inn</a></li>
<li><a href="/buy">Buy Weapons/Armor</a></li>
<li><a hx-get="/inn" hx-target="#middle">Rest at the Inn</a></li>
<li><a hx-get="/buy" hx-target="#middle">Buy Weapons/Armor</a></li>
<li><a href="/maps">Buy Maps</a></li>
</ul>
</div>