'.page_title().'';
- $content .= Render\debug_db_info();
- if (env('debug', false)) $content .= Render\debug_query_log();
-
- if ($GLOBALS['state']['user-state-changed'] ?? false) {
- $content .= Render\right_nav();
- $content .= Render\left_nav();
- }
-}
-echo $content;
+echo render_response($uri, $l['handler'](...$l['params'] ?? []));
exit;
/**
@@ -96,7 +84,10 @@ function show_character_info(int $id = 0): string
return render('layouts/minimal', ['content' => $showchar, 'title' => $user->username.' Information']);
}
-function showmap()
+/**
+ * Show the user their position on the current world map. Only works with a game size of 250 and the default towns 😅
+ */
+function show_map()
{
$pos = sprintf(
'',
@@ -111,7 +102,7 @@ function showmap()
}
/**
- * ...
+ * Handle a POST request to send a new babblebox message.
*/
function babblebox()
{
diff --git a/src/actions/admin.php b/src/actions/admin.php
index a7eb045..776fc24 100644
--- a/src/actions/admin.php
+++ b/src/actions/admin.php
@@ -8,16 +8,16 @@ use Router;
function register_routes(Router $r): Router
{
- if (user('authlevel') === 1) {
+ if (user()->authlevel === 1) {
$r->get('/admin', 'Admin\donothing');
$r->form('/admin/main', 'Admin\primary');
$r->get('/admin/items', 'Admin\items');
- $r->form('/admin/items/:id', 'Admin\edititem');
+ $r->form('/admin/items/:id', 'Admin\edit_item');
$r->get('/admin/drops', 'Admin\drops');
- $r->form('/admin/drops/:id', 'Admin\editdrop');
+ $r->form('/admin/drops/:id', 'Admin\edit_drop');
$r->get('/admin/towns', 'Admin\towns');
$r->form('/admin/towns/:id', 'Admin\edittown');
@@ -39,18 +39,19 @@ function register_routes(Router $r): Router
return $r;
}
-function donothing()
+/**
+ * Home page for the admin panel.
+ */
+function donothing(): string
{
$page = <<
Please note that the control panel has been created mostly as a shortcut for certain individual settings. It is
meant for use primarily with editing one thing at a time. If you need to completely replace an entire table
(say, to replace all stock monsters with your own new ones), it is suggested that you use a more in-depth
- database tool such as phpMyAdmin. Also, you may want
- to have a copy of the Dragon Knight development kit, available from the
- Dragon Knight homepage.
+ database tool such as SQLite Browser.
Also, you should be aware that certain portions of the DK code are dependent on the formatting of certain
database results (for example, the special attributes on item drops). While I have attempted to point these out
@@ -58,18 +59,22 @@ function donothing()
because mistakes in the database content may result in script errors or your game breaking completely.
HTML;
- display_admin($page, "Admin Home");
+ page_title('Admin');
+ return \Render\content($page, 'layouts/admin');
}
-function primary()
+/**
+ * Main settings that get written to .env
+ */
+function primary(): string
{
- if (isset($_POST["submit"])) {
+ if (is_post()) {
$form = validate($_POST, [
- 'gamename' => ['alphanum-spaces', 'length:1-20'],
+ 'gamename' => ['alphanum-spaces'],
'gamesize' => ['int', 'min:5'],
- 'class1name' => ['alpha-spaces', 'length:1-18'],
- 'class2name' => ['alpha-spaces', 'length:1-18'],
- 'class3name' => ['alpha-spaces', 'length:1-18'],
+ 'class1name' => ['alpha-spaces'],
+ 'class2name' => ['alpha-spaces'],
+ 'class3name' => ['alpha-spaces'],
'gameopen' => ['bool'],
'verifyemail' => ['bool'],
'shownews' => ['bool'],
@@ -81,203 +86,153 @@ function primary()
$form = $form['data'];
if (($form['gamesize'] % 5) != 0) exit('Map size must be divisible by five.');
- db()->query('UPDATE control SET gamename=?, gamesize=?, class1name=?, class2name=?, class3name=?, gameopen=?, verifyemail=?, gameurl=?, adminemail=?, shownews=?, showonline=?, showbabble=? WHERE id=1;', [
- $form['gamename'], $form['gamesize'], $form['class1name'], $form['class2name'], $form['class3name'], $form['gameopen'], $form['verifyemail'], $form['gameurl'], $form['adminemail'], $form['shownews'], $form['showonline'], $form['showbabble']
- ]);
+ // @todo
+ // write changes to .env
- display_admin("Settings updated.", "Main Settings");
+ $page = 'Main settings updated.';
} else {
- $errorlist = ul_from_validate_errors($form['errors']);
- display_admin("Errors:
$errorlist
Please go back and try again.", "Main Settings");
+ $error_list = ul_from_validate_errors($form['errors']);
+ $page = <<Errors:
+
{$error_list}
+ Please go back and try again.
+ HTML;
}
- }
+ } else {
+ $page = render('admin/main_settings');
+ }
- global $controlrow;
-
- $page = <<Main Settings
- These options control several major settings for the overall game engine.
-
- HTML;
-
- if ($controlrow["verifyemail"] == 0) { $controlrow["selectverify0"] = "selected=\"selected\" "; } else { $controlrow["selectverify0"] = ""; }
- if ($controlrow["verifyemail"] == 1) { $controlrow["selectverify1"] = "selected=\"selected\" "; } else { $controlrow["selectverify1"] = ""; }
- if ($controlrow["shownews"] == 0) { $controlrow["selectnews0"] = "selected=\"selected\" "; } else { $controlrow["selectnews0"] = ""; }
- if ($controlrow["shownews"] == 1) { $controlrow["selectnews1"] = "selected=\"selected\" "; } else { $controlrow["selectnews1"] = ""; }
- if ($controlrow["showonline"] == 0) { $controlrow["selectonline0"] = "selected=\"selected\" "; } else { $controlrow["selectonline0"] = ""; }
- if ($controlrow["showonline"] == 1) { $controlrow["selectonline1"] = "selected=\"selected\" "; } else { $controlrow["selectonline1"] = ""; }
- if ($controlrow["showbabble"] == 0) { $controlrow["selectbabble0"] = "selected=\"selected\" "; } else { $controlrow["selectbabble0"] = ""; }
- if ($controlrow["showbabble"] == 1) { $controlrow["selectbabble1"] = "selected=\"selected\" "; } else { $controlrow["selectbabble1"] = ""; }
- if ($controlrow["gameopen"] == 1) { $controlrow["open1select"] = "selected=\"selected\" "; } else { $controlrow["open1select"] = ""; }
- if ($controlrow["gameopen"] == 0) { $controlrow["open0select"] = "selected=\"selected\" "; } else { $controlrow["open0select"] = ""; }
-
- display_admin(parse($page, $controlrow), "Main Settings");
+ page_title('Admin: Main Settings');
+ return \Render\content($page, 'layouts/admin');
}
-function items()
+/**
+ * Show the full list of items that can be edited.
+ */
+function items(): string
{
$items = db()->query('SELECT id, name FROM items ORDER BY id;');
- $page = "Edit Items Click an item's name to edit it.
', 'layouts/admin');
}
-function edititem($id)
+/**
+ * Shows the form for editing an item via GET, processes edits via POST
+ */
+function edit_item(int $id): string
{
- if (isset($_POST["submit"])) {
- $errors = [];
- $n = trim($_POST['name'] ?? '');
- $bc = (int) trim($_POST['buycost'] ?? 0);
- $a = (int) trim($_POST['attribute'] ?? 0);
- $s = trim($_POST['special'] ?? 'X');
-
- if (empty($n)) $errors[] = "Name is required.";
- if (!is_int($bc) || !($bc >= 0)) $errors[] = 'Cost must be a number greater than or equal to 0.';
- if (!is_int($a)) $errors[] = 'Attribute must be a number.';
-
- if (count($errors) === 0) {
- db()->query('UPDATE items SET name=?, type=?, buycost=?, attribute=?, special=? WHERE id=?;', [
- $n, $_POST['type'] ?? 0, $bc, $a, $s, $id
- ]);
- display_admin("Item updated.","Edit Items");
- } else {
- $errorlist = implode(' ', $errors);
- display_admin("Errors:
$errorlist
Please go back and try again.", "Edit Items");
- }
- }
-
$item = get_item($id);
- $page = <<Edit Items
-
- Special Codes:
- Special codes can be added in the item's Special field to give it extra user attributes. Special codes are in the format attribute,value. Attribute can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. Value may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be maxhp,50.
- Suggested user fields for special codes:
- maxhp - max hit points
- maxmp - max magic points
- maxtp - max travel points
- goldbonus - gold bonus, in percent
- expbonus - experience bonus, in percent
- strength - strength (which also adds to attackpower)
- dexterity - dexterity (which also adds to defensepower)
- attackpower - total attack power
- defensepower - total defense power
- HTML;
+ if (is_post()) {
+ $form = validate($_POST, [
+ 'name' => [],
+ 'type' => ['int', 'in:1,2,3'],
+ 'buycost' => ['int', 'min:0'],
+ 'attribute' => ['int', 'min:0'],
+ 'special' => ['default:X']
+ ]);
- if ($item["type"] == 1) { $item["type1select"] = "selected=\"selected\" "; } else { $item["type1select"] = ""; }
- if ($item["type"] == 2) { $item["type2select"] = "selected=\"selected\" "; } else { $item["type2select"] = ""; }
- if ($item["type"] == 3) { $item["type3select"] = "selected=\"selected\" "; } else { $item["type3select"] = ""; }
+ if ($form['valid']) {
+ $f = $form['data'];
+ db()->query('UPDATE items SET name=?, type=?, buycost=?, attribute=?, special=? WHERE id=?;', [
+ $f['name'], $f['type'], $f['buycost'], $f['attribute'], $f['special'], $id
+ ]);
+ $page = ''.$item['name'].' updated.';
+ } else {
+ $error_list = ul_from_validate_errors($form['errors']);
+ $page = <<Errors:
+
{$error_list}
+ Please go back and try again.
+ HTML;
+ }
+ } else {
+ $page = render('admin/edit_item', ['item' => $item]);
+ }
- display_admin(parse($page, $item), "Edit Items");
+ page_title('Admin: Editing '.$item['name']);
+ return \Render\content($page, 'layouts/admin');
}
+/**
+ * Show the full list of drops that can be edited
+ */
function drops()
{
- $page = "Edit Drops Click an item's name to edit it.
\n";
+ $page = "
Edit Drops
Click an item's name to edit it.
\n";
$drops = db()->query('SELECT id, name FROM drops ORDER BY id;');
$has_drops = false;
while ($row = $drops->fetchArray(SQLITE3_ASSOC)) {
$has_drops = true;
- $page .= "
', 'layouts/admin');
}
-function editdrop($id)
+/**
+ * Show the form to edit drops via GET, process those edits via POST
+ */
+function edit_drop(int $id): string
{
- if (isset($_POST["submit"])) {
- $errors = [];
-
- $n = trim($_POST['name'] ?? '');
- $ml = (int) trim($_POST['mlevel'] ?? 0);
- $a = trim($_POST['attribute1'] ?? 'X');
- $a2 = trim($_POST['attribute2'] ?? 'X');
-
- if (empty($n)) $errors[] = "Name is required.";
- if (!is_int($ml) || $ml < 1) $errors[] = "Monster level is required, and must be higher than 0.";
- if (empty($a) || $a === 'X') $errors[] = 'First attribute is required.';
- if (empty($a2)) $a2 = 'X';
-
- if (count($errors) === 0) {
- db()->query('UPDATE drops SET name=?, mlevel=?, attribute1=?, attribute2=? WHERE id=?;', [
- $n, $ml, $a, $a2, $id
- ]);
- display_admin("Item updated.","Edit Drops");
- } else {
- $errorlist = implode(' ', $errors);
- display_admin("Errors:
$errorlist
Please go back and try again.", "Edit Drops");
- }
- }
-
$drop = get_drop($id);
- $page = <<Edit Drops
-
- Special Codes:
- Special codes are used in the two attribute fields to give the item properties. The first attribute field must contain a special code, but the second one may be left empty ("X") if you wish. Special codes are in the format attribute,value. Attribute can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. Value may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be maxhp,50.
- Suggested user fields for special codes:
- maxhp - max hit points
- maxmp - max magic points
- maxtp - max travel points
- goldbonus - gold bonus, in percent
- expbonus - experience bonus, in percent
- strength - strength (which also adds to attackpower)
- dexterity - dexterity (which also adds to defensepower)
- attackpower - total attack power
- defensepower - total defense power
- HTML;
+ if (is_post()) {
+ $form = validate($_POST, [
+ 'name' => [],
+ 'mlevel' => ['int', 'min:1'],
+ 'attribute1' => [],
+ 'attribute2' => ['default:X'],
+ ]);
- display_admin(parse($page, $drop), "Edit Drops");
+ if ($form['valid']) {
+ db()->query('UPDATE drops SET name=?, mlevel=?, attribute1=?, attribute2=? WHERE id=?;', [
+ $form['data']['name'], $form['data']['mlevel'], $form['data']['attribute1'], $form['data']['attribute2'], $id
+ ]);
+ $page = ''.$form['data']['name'].' updated.';
+ } else {
+ $error_list = ul_from_validate_errors($form['errors']);
+ $page = <<Errors:
+
{$error_list}
+ Please go back and try again.
+ HTML;
+ }
+ } else {
+ $page = render('admin/edit_drop', ['drop' => $drop]);
+ }
+
+ page_title('Admin: Editing '.$drop['name']);
+ return \Render\content($page, 'layouts/admin');
}
+/**
+ * Generate the list of towns that can be edited.
+ */
function towns()
{
$page = "Edit Towns Click an town's name to edit it.
\n";
diff --git a/src/lib.php b/src/lib.php
index baf8800..4825697 100644
--- a/src/lib.php
+++ b/src/lib.php
@@ -82,11 +82,7 @@ function display_admin($content, $title)
{
echo render('layouts/admin', [
"title" => $title,
- "content" => $content,
- "totaltime" => round(microtime(true) - START, 4),
- "numqueries" => db()->count,
- "version" => VERSION,
- "build" => BUILD
+ "content" => $content
]);
exit;
@@ -511,3 +507,29 @@ function page_title(string $new_title = ''): string
if ($new_title) return $GLOBALS['state']['new-page-title'] = $new_title;
return $GLOBALS['state']['new-page-title'] ?? env('game_name');
}
+
+/**
+ * Render the response for the browser based on the request context. The main point is to seperate the handling
+ * of HTMX responses from normal responses.
+ */
+function render_response(array $uri, string $content): string
+{
+ if (!is_htmx() || $uri[0] === 'babblebox') return $content;
+
+ header('HX-Push-Url: '.$_SERVER['REQUEST_URI']);
+
+ $content .= ''.page_title().'';
+
+ $content .= Render\debug_db_info();
+
+ if (env('debug', false)) {
+ $content .= Render\debug_query_log();
+ }
+
+ if ($GLOBALS['state']['user-state-changed'] ?? false) {
+ $content .= Render\right_nav();
+ $content .= Render\left_nav();
+ }
+
+ return $content;
+}
diff --git a/src/models/user.php b/src/models/user.php
index f448a2c..8356f7d 100644
--- a/src/models/user.php
+++ b/src/models/user.php
@@ -43,6 +43,7 @@ class User extends Model
*/
public function update_online_time(): void
{
+ if ($this->onlinetime && strtotime($this->onlinetime) > strtotime('-9 minutes')) return;
db()->query('UPDATE users SET onlinetime=CURRENT_TIMESTAMP WHERE id=?;', [$this->id]);
}
diff --git a/src/render.php b/src/render.php
index 3814394..ac63bfb 100644
--- a/src/render.php
+++ b/src/render.php
@@ -9,12 +9,12 @@ namespace Render;
/**
* Prepare content for final render. If the request is HTMX-based, will return just the content passed to it. Otherwise
- * it will render() onto layouts/primary with some additional bits.
+ * it will render() onto $layout with some additional bits.
*/
-function content(string $content): string
+function content(string $content, string $layout = 'layouts/primary'): string
{
if (is_htmx()) return $content;
- return render('layouts/primary', ['content' => $content]);
+ return render($layout, ['content' => $content]);
}
function debug_db_info(): string {
diff --git a/templates/admin/edit_drop.php b/templates/admin/edit_drop.php
new file mode 100644
index 0000000..6946753
--- /dev/null
+++ b/templates/admin/edit_drop.php
@@ -0,0 +1,49 @@
+
Editing = $drop['name'] ?>
+
+
+
+
+
Special Codes
+Special codes are used in the two attribute fields to give the item properties. The first attribute field must contain a special code, but the second one may be left empty ("X") if you wish. Special codes are in the format attribute,value. Attribute can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. Value may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be maxhp,50.
+Suggested user fields for special codes:
+maxhp - max hit points
+maxmp - max magic points
+maxtp - max travel points
+goldbonus - gold bonus, in percent
+expbonus - experience bonus, in percent
+strength - strength (which also adds to attackpower)
+dexterity - dexterity (which also adds to defensepower)
+attackpower - total attack power
+defensepower - total defense power
diff --git a/templates/admin/edit_item.php b/templates/admin/edit_item.php
new file mode 100644
index 0000000..708fee5
--- /dev/null
+++ b/templates/admin/edit_item.php
@@ -0,0 +1,55 @@
+
Edit = $item['name'] ?>
+
+
+
+
+
Special Codes
+Special codes can be added in the item's Special field to give it extra user attributes. Special codes are in the format attribute,value. Attribute can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. Value may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be maxhp,50.
+Suggested user fields for special codes:
+maxhp - max hit points
+maxmp - max magic points
+maxtp - max travel points
+goldbonus - gold bonus, in percent
+expbonus - experience bonus, in percent
+strength - strength (which also adds to attackpower)
+dexterity - dexterity (which also adds to defensepower)
+attackpower - total attack power
+defensepower - total defense power
diff --git a/templates/admin/main_settings.php b/templates/admin/main_settings.php
new file mode 100644
index 0000000..6351c23
--- /dev/null
+++ b/templates/admin/main_settings.php
@@ -0,0 +1,110 @@
+
Main Settings
+
These options control several major settings for the game engine.
+
Note that these particular settings are written to the .env file in the root directory, and not the database.