From 4d5c345528210fca485ff358ed2722b9388b42d0 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 14 Aug 2025 15:47:25 -0400 Subject: [PATCH] functions.php --- composer.json | 4 + public/index.php | 16 +- src/DragonKnight/Actions/Admin.php | 158 +++--- src/DragonKnight/Actions/Explore.php | 41 +- src/DragonKnight/Actions/Fight.php | 277 +++++----- src/DragonKnight/Actions/Forum.php | 35 +- src/DragonKnight/Actions/Heal.php | 20 +- src/DragonKnight/Actions/Help.php | 21 +- src/DragonKnight/Actions/Install.php | 62 +-- src/DragonKnight/Actions/Towns.php | 154 +++--- src/DragonKnight/Auth.php | 2 +- src/DragonKnight/DragonKnight.php | 18 + src/DragonKnight/Lib.php | 731 --------------------------- src/DragonKnight/Models/Model.php | 4 +- src/DragonKnight/Models/User.php | 10 +- src/DragonKnight/Render.php | 30 +- src/DragonKnight/functions.php | 724 ++++++++++++++++++++++++++ templates/admin/edit_level.php | 2 +- templates/admin/edit_user.php | 6 +- templates/admin/main_settings.php | 34 +- templates/layouts/admin.php | 6 +- templates/layouts/help.php | 4 +- templates/layouts/primary.php | 10 +- templates/left_nav.php | 16 +- templates/register.php | 6 +- templates/right_nav.php | 26 +- templates/show_char.php | 6 +- 27 files changed, 1232 insertions(+), 1191 deletions(-) create mode 100644 src/DragonKnight/DragonKnight.php delete mode 100644 src/DragonKnight/Lib.php create mode 100644 src/DragonKnight/functions.php diff --git a/composer.json b/composer.json index 7b643d4..5757052 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,10 @@ "team-reflex/discord-php": "^10.18.45" }, "autoload": { + "files": [ + "src/DragonKnight/functions.php", + "src/DragonKnight/DragonKnight.php" + ], "psr-4": { "DragonKnight\\": "src/DragonKnight" } diff --git a/public/index.php b/public/index.php index 3d485ac..ceaacb2 100644 --- a/public/index.php +++ b/public/index.php @@ -21,20 +21,20 @@ use DragonKnight\Admin\Admin; // Do an early return with babblebox data if that's what's being requested if ($uri[0] === 'babblebox' && (isset($uri[1]) && $uri[1] === 'messages')) { - echo Lib::babblebox_messages(); + echo babblebox_messages(); exit; } $r = new Router; -$r->get('/', 'DragonKnight\Lib::index'); +$r->get('/', 'DragonKnight\index'); $r->post('/move', 'DragonKnight\Actions\Explore::move'); $r->get('/spell/:id', 'DragonKnight\Actions\Heal::healspells'); -$r->get('/character', 'DragonKnight\Lib::show_character_info'); -$r->get('/character/:id', 'DragonKnight\Lib::show_character_info'); -$r->get('/showmap', 'DragonKnight\Lib::show_map'); -$r->form('/babblebox', 'DragonKnight\Lib::babblebox'); -$r->get('/babblebox/messages', 'DragonKnight\Lib::babblebox_messages'); +$r->get('/character', 'DragonKnight\show_character_info'); +$r->get('/character/:id', 'DragonKnight\show_character_info'); +$r->get('/showmap', 'DragonKnight\show_map'); +$r->form('/babblebox', 'DragonKnight\babblebox'); +$r->get('/babblebox/messages', 'DragonKnight\babblebox_messages'); Towns::register_routes($r); Fight::register_routes($r); @@ -57,4 +57,4 @@ $l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']); if (is_int($l)) { exit("Error: $l"); } -echo Lib::render_response($uri, $l['handler'](...$l['params'] ?? [])); +echo render_response($uri, $l['handler'](...$l['params'] ?? [])); diff --git a/src/DragonKnight/Actions/Admin.php b/src/DragonKnight/Actions/Admin.php index 9de4de4..de4e027 100644 --- a/src/DragonKnight/Actions/Admin.php +++ b/src/DragonKnight/Actions/Admin.php @@ -13,15 +13,29 @@ declare(strict_types=1); namespace DragonKnight\Admin; -use DragonKnight\Lib; use DragonKnight\Router; use SQLite3Result; +use function DragonKnight\db; +use function DragonKnight\env; +use function DragonKnight\get_drop; +use function DragonKnight\get_item; +use function DragonKnight\get_monster; +use function DragonKnight\get_spell; +use function DragonKnight\get_town_by_id; +use function DragonKnight\is_post; +use function DragonKnight\make_safe; +use function DragonKnight\page_title; +use function DragonKnight\render; +use function DragonKnight\ul_from_validate_errors; +use function DragonKnight\user; +use function DragonKnight\validate; + class Admin { public static function register_routes(Router $r): Router { - if (Lib::user() !== false && Lib::user()->authlevel === 1) { + if (user() !== false && user()->authlevel === 1) { $r->get('/admin', 'Admin\donothing'); $r->form('/admin/main', 'Admin\primary'); @@ -58,7 +72,7 @@ class Admin */ public static function donothing(): string { - Lib::page_title('Admin'); + page_title('Admin'); return << ['alphanum-spaces'], 'gamesize' => ['int', 'min:5'], 'class1name' => ['alpha-spaces'], @@ -106,7 +120,7 @@ class Admin $page = 'Main settings updated.'; } else { - $error_list = Lib::ul_from_validate_errors($form['errors']); + $error_list = ul_from_validate_errors($form['errors']); $page = <<Errors:
{$error_list}

@@ -114,10 +128,10 @@ class Admin HTML; } } else { - $page = Lib::render('admin/main_settings'); + $page = render('admin/main_settings'); } - Lib::page_title('Admin: Main Settings'); + page_title('Admin: Main Settings'); return $page; } @@ -127,11 +141,11 @@ class Admin */ public static function items(): string { - $items = Lib::db()->query('SELECT * FROM items ORDER BY id;'); + $items = db()->query('SELECT * FROM items ORDER BY id;'); $page = "

Edit Items

Click an item's name or ID to edit it.

\n"; $page .= self::build_bulk_table($items, 'name', '/admin/items'); - Lib::page_title('Admin: Items'); + page_title('Admin: Items'); return $page; } @@ -141,19 +155,19 @@ class Admin */ public static function edit_item(int $id): string { - $item = Lib::get_item($id); + $item = get_item($id); - $page = Lib::is_post() - ? self::handle_edit_form($id, 'items', Lib::validate($_POST, [ + $page = is_post() + ? self::handle_edit_form($id, 'items', validate($_POST, [ 'name' => [], 'type' => ['int', 'in:1,2,3'], 'buycost' => ['int', 'min:0'], 'attribute' => ['int', 'min:0'], 'special' => ['default:X'], ])) - : Lib::render('admin/edit_item', ['item' => $item]); + : render('admin/edit_item', ['item' => $item]); - Lib::page_title('Admin: Editing '.$item['name']); + page_title('Admin: Editing '.$item['name']); return $page; } @@ -163,11 +177,11 @@ class Admin */ public static function drops() { - $drops = Lib::db()->query('SELECT * FROM drops ORDER BY id;'); + $drops = db()->query('SELECT * FROM drops ORDER BY id;'); $page = "

Edit Drops

Click an item's name to edit it.

\n"; $page .= self::build_bulk_table($drops, 'name', '/admin/drops'); - Lib::page_title('Admin: Drops'); + page_title('Admin: Drops'); return $page; } @@ -177,20 +191,20 @@ class Admin */ public static function edit_drop(int $id): string { - $drop = Lib::get_drop($id); + $drop = get_drop($id); - if (Lib::is_post()) { - $page = self::handle_edit_form($id, 'drops', Lib::validate($_POST, [ + if (is_post()) { + $page = self::handle_edit_form($id, 'drops', validate($_POST, [ 'name' => [], 'mlevel' => ['int', 'min:1'], 'attribute1' => [], 'attribute2' => ['default:X'], ])); } else { - $page = Lib::render('admin/edit_drop', ['drop' => $drop]); + $page = render('admin/edit_drop', ['drop' => $drop]); } - Lib::page_title('Admin: Editing '.$drop['name']); + page_title('Admin: Editing '.$drop['name']); return $page; } @@ -200,11 +214,11 @@ class Admin */ public static function towns(): string { - $towns = Lib::db()->query('SELECT * FROM towns ORDER BY id;'); + $towns = db()->query('SELECT * FROM towns ORDER BY id;'); $page = "

Edit Towns

Click an town's name or ID to edit it.

\n"; $page .= self::build_bulk_table($towns, 'name', '/admin/towns'); - Lib::page_title('Admin: Towns'); + page_title('Admin: Towns'); return $page; } @@ -214,23 +228,23 @@ class Admin */ public static function edit_town(int $id): string { - $town = Lib::get_town_by_id($id); + $town = get_town_by_id($id); - if (Lib::is_post()) { - $page = self::handle_edit_form($id, 'towns', Lib::validate($_POST, [ + if (is_post()) { + $page = self::handle_edit_form($id, 'towns', validate($_POST, [ 'name' => [], - 'latitude' => ['int', 'min:0', 'max:'.Lib::env('game_size')], - 'longitude' => ['int', 'min:0', 'max:'.Lib::env('game_size')], + 'latitude' => ['int', 'min:0', 'max:'.env('game_size')], + 'longitude' => ['int', 'min:0', 'max:'.env('game_size')], 'innprice' => ['int', 'min:0'], 'mapprice' => ['int', 'min:0'], 'travelpoints' => ['int', 'min:0'], 'itemslist' => ['optional'], ])); } else { - $page = Lib::render('admin/edit_town', ['town' => $town]); + $page = render('admin/edit_town', ['town' => $town]); } - Lib::page_title('Admin: Editing '.$town['name']); + page_title('Admin: Editing '.$town['name']); return $page; } @@ -240,19 +254,19 @@ class Admin */ public static function monsters() { - $max_level = Lib::db()->query('SELECT level FROM monsters ORDER BY level DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['level']; - $monsters = Lib::db()->query('SELECT * FROM monsters ORDER BY id;'); + $max_level = db()->query('SELECT level FROM monsters ORDER BY level DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['level']; + $monsters = db()->query('SELECT * FROM monsters ORDER BY id;'); $page = '

Edit Monsters

'; - $page .= ((Lib::env('game_size') / 5) !== $max_level) - ? 'Note: Your highest monster level does not match with your entered map size. Highest monster level should be '.(Lib::env('game_size') / 5).", yours is $max_level. Please fix this before opening the game to the public.
" + $page .= ((env('game_size') / 5) !== $max_level) + ? 'Note: Your highest monster level does not match with your entered map size. Highest monster level should be '.(env('game_size') / 5).", yours is $max_level. Please fix this before opening the game to the public.
" : 'Monster level and map size match. No further actions are required for map compatibility.
'; $page .= "Click an monster's name or ID to edit it.

\n"; $page .= self::build_bulk_table($monsters, 'name', '/admin/monsters'); - Lib::page_title('Admin: Monsters'); + page_title('Admin: Monsters'); return $page; } @@ -262,10 +276,10 @@ class Admin */ public static function edit_monster(int $id): string { - $monster = Lib::get_monster($id); + $monster = get_monster($id); - $page = (Lib::is_post()) - ? self::handle_edit_form($id, 'monsters', Lib::validate($_POST, [ + $page = (is_post()) + ? self::handle_edit_form($id, 'monsters', validate($_POST, [ 'name' => [], 'maxhp' => ['int', 'min:1'], 'maxdam' => ['int', 'min:0'], @@ -275,9 +289,9 @@ class Admin 'maxgold' => ['int', 'min:0'], 'immune' => ['in:0,1,2'], ])) - : Lib::render('admin/edit_monster', ['monster' => $monster]); + : render('admin/edit_monster', ['monster' => $monster]); - Lib::page_title('Admin: Editing '.$monster['name']); + page_title('Admin: Editing '.$monster['name']); return $page; } @@ -289,10 +303,10 @@ class Admin { $page = "

Edit Spells

Click an spell's name to edit it.

\n"; - $spells = Lib::db()->query('SELECT * FROM spells ORDER BY id;'); + $spells = db()->query('SELECT * FROM spells ORDER BY id;'); $page .= self::build_bulk_table($spells, 'name', '/admin/spells'); - Lib::page_title('Admin: Spells'); + page_title('Admin: Spells'); return $page; } @@ -302,18 +316,18 @@ class Admin */ public static function edit_spell(int $id): string { - $spell = Lib::get_spell($id); + $spell = get_spell($id); - $page = (Lib::is_post()) - ? self::handle_edit_form($id, 'spells', Lib::validate($_POST, [ + $page = (is_post()) + ? self::handle_edit_form($id, 'spells', validate($_POST, [ 'name' => [], 'mp' => ['int', 'min:0'], 'attribute' => ['int', 'min:0'], 'type' => ['in:1,2,3,4,5'], ])) - : Lib::render('admin/edit_spell', ['spell' => $spell]); + : render('admin/edit_spell', ['spell' => $spell]); - Lib::page_title('Admin: Editing '.$spell['name']); + page_title('Admin: Editing '.$spell['name']); return $page; } @@ -323,7 +337,7 @@ class Admin */ public static function levels(): string { - $max_level = Lib::db()->query('SELECT id FROM levels ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['id']; + $max_level = db()->query('SELECT id FROM levels ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC)['id']; $page = <<Edit Levels @@ -340,7 +354,7 @@ class Admin HTML; - Lib::page_title('Admin: Levels'); + page_title('Admin: Levels'); return $page; } @@ -354,13 +368,13 @@ class Admin return 'No level to edit.'; } $id = $_POST['level']; - $level = Lib::db()->query('SELECT * FROM levels WHERE id=? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); + $level = db()->query('SELECT * FROM levels WHERE id=? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); - if (Lib::is_post() && isset($_POST['save'])) { + if (is_post() && isset($_POST['save'])) { unset($_POST['save']); unset($_POST['level']); - $page = self::handle_edit_form($id, 'levels', Lib::validate($_POST, [ + $page = self::handle_edit_form($id, 'levels', validate($_POST, [ '1_exp' => ['int', 'min:0'], '1_hp' => ['int', 'min:0'], '1_mp' => ['int', 'min:0'], @@ -384,21 +398,21 @@ class Admin '3_spells' => ['int', 'min:0'], ]), 'Level '.$id.' updated.'); } else { - $page = Lib::render('admin/edit_level', ['level' => $level]); + $page = render('admin/edit_level', ['level' => $level]); } - Lib::page_title('Admin: Editing Level '.$id); + page_title('Admin: Editing Level '.$id); return $page; } public static function users() { - $users = Lib::db()->query('SELECT * FROM users ORDER BY id;'); + $users = db()->query('SELECT * FROM users ORDER BY id;'); $page = '

Edit Users

Click a username or ID to edit the account.

'; $page .= self::build_bulk_table($users, 'username', '/admin/users'); - Lib::page_title('Admin: Users'); + page_title('Admin: Users'); return $page.'
'; } @@ -408,17 +422,17 @@ class Admin */ public static function edit_user(int $id): string { - $user = Lib::db()->query('SELECT * FROM users WHERE id = ? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); + $user = db()->query('SELECT * FROM users WHERE id = ? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); - if (Lib::is_post()) { - $form = Lib::validate($_POST, [ + if (is_post()) { + $form = validate($_POST, [ 'username' => ['length:3-18', 'alpha-spaces', 'unique:users,username'], 'verify' => [], 'authlevel' => ['int'], 'email' => ['email', 'unique:users,email'], 'charclass' => ['in:1,2,3'], - 'latitude' => ['int', 'min:0', 'max:'.Lib::env('game_size')], - 'longitude' => ['int', 'min:0', 'max:'.Lib::env('game_size')], + 'latitude' => ['int', 'min:0', 'max:'.env('game_size')], + 'longitude' => ['int', 'min:0', 'max:'.env('game_size')], 'currentaction' => [], 'currentfight' => ['int'], 'currentmonster' => ['int'], @@ -463,7 +477,7 @@ class Admin self::save_data_row('users', $form['data'], $id); $page = 'User '.$user['username'].' updated.'; } else { - $error_list = Lib::ul_from_validate_errors($form['errors']); + $error_list = ul_from_validate_errors($form['errors']); $page = <<Errors:
{$error_list}

@@ -471,10 +485,10 @@ class Admin HTML; } } else { - $page = Lib::render('admin/edit_user', ['user' => $user]); + $page = render('admin/edit_user', ['user' => $user]); } - Lib::page_title('Admin: Editing '.$user['username']); + page_title('Admin: Editing '.$user['username']); return $page; } @@ -484,7 +498,7 @@ class Admin */ public static function add_news() { - if (Lib::is_post()) { + if (is_post()) { $c = trim($_POST['content'] ?? ''); $errors = []; @@ -493,7 +507,7 @@ class Admin } if (count($errors) === 0) { - Lib::db()->query('INSERT INTO news (author, content) VALUES (?, ?);', [Lib::user()->username, $c]); + db()->query('INSERT INTO news (author, content) VALUES (?, ?);', [user()->username, $c]); $page = 'News post added.'; } else { $error_list = implode('
', $errors); @@ -511,7 +525,7 @@ class Admin HTML; } - Lib::page_title('Admin: Add News'); + page_title('Admin: Add News'); return $page; } @@ -541,7 +555,7 @@ class Admin foreach ($columns as $column) { $html_parts[] = ''. - Lib::make_safe($column === 'id' ? 'ID' : ucfirst($column)). + make_safe($column === 'id' ? 'ID' : ucfirst($column)). ''; } $html_parts[] = ''; @@ -551,7 +565,7 @@ class Admin foreach ($data as $row) { $html_parts[] = ''; foreach ($columns as $column) { - $name = Lib::make_safe($row[$column]); + $name = make_safe($row[$column]); $html_parts[] = isset($is_edit_column[$column]) ? "{$name}" : "{$name}"; @@ -578,7 +592,7 @@ class Admin $values = array_values($data); $values[] = $id; - return Lib::db()->query("UPDATE $table SET $fields WHERE id=?", $values); + return db()->query("UPDATE $table SET $fields WHERE id=?", $values); } /** @@ -590,7 +604,7 @@ class Admin self::save_data_row($table, $form['data'], $id); $page = $updated_message ?: ''.$form['data']['name'].' updated.'; } else { - $error_list = Lib::ul_from_validate_errors($form['errors']); + $error_list = ul_from_validate_errors($form['errors']); $page = <<Errors:
{$error_list}

diff --git a/src/DragonKnight/Actions/Explore.php b/src/DragonKnight/Actions/Explore.php index 531fff2..211e1d1 100644 --- a/src/DragonKnight/Actions/Explore.php +++ b/src/DragonKnight/Actions/Explore.php @@ -13,7 +13,14 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; +use function DragonKnight\env; +use function DragonKnight\get_town_by_xy; +use function DragonKnight\index; +use function DragonKnight\page_title; +use function DragonKnight\redirect; +use function DragonKnight\ul_from_validate_errors; +use function DragonKnight\user; +use function DragonKnight\validate; class Explore { @@ -23,7 +30,7 @@ class Explore */ public static function explore() { - Lib::page_title('Exploring'); + page_title('Exploring'); return <<Exploring @@ -34,20 +41,20 @@ class Explore public static function move() { // Early exit if fighting - if (Lib::user()->currentaction == 'Fighting') { - Lib::redirect('/fight'); + if (user()->currentaction == 'Fighting') { + redirect('/fight'); } // Validate direction - $form = Lib::validate($_POST, ['direction' => ['in:north,west,east,south']]); + $form = validate($_POST, ['direction' => ['in:north,west,east,south']]); if (! $form['valid']) { - return Lib::ul_from_validate_errors($form['errors']); + return ul_from_validate_errors($form['errors']); } // Current game state - $game_size = Lib::env('game_size'); - $latitude = Lib::user()->latitude; - $longitude = Lib::user()->longitude; + $game_size = env('game_size'); + $latitude = user()->latitude; + $longitude = user()->longitude; $direction = $form['data']['direction']; // Calculate new coordinates with boundary checks @@ -67,23 +74,23 @@ class Explore } // Check for town - $town = Lib::get_town_by_xy($longitude, $latitude); + $town = get_town_by_xy($longitude, $latitude); if ($town !== false) { return Towns::travelto($town['id'], false); } // Determine action (1 in 5 chance of fighting) if (rand(1, 5) === 1) { - Lib::user()->currentaction = 'Fighting'; - Lib::user()->currentfight = 1; + user()->currentaction = 'Fighting'; + user()->currentfight = 1; } else { - Lib::user()->currentaction = 'Exploring'; + user()->currentaction = 'Exploring'; } - Lib::user()->latitude = $latitude; - Lib::user()->longitude = $longitude; - Lib::user()->save(); + user()->latitude = $latitude; + user()->longitude = $longitude; + user()->save(); - return Lib::index(); + return index(); } } diff --git a/src/DragonKnight/Actions/Fight.php b/src/DragonKnight/Actions/Fight.php index 90d1fe6..a3a7786 100644 --- a/src/DragonKnight/Actions/Fight.php +++ b/src/DragonKnight/Actions/Fight.php @@ -13,7 +13,6 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; use DragonKnight\Router; class Fight @@ -33,7 +32,7 @@ class Fight */ public static function fight() { - if (Lib::user()->currentaction !== 'Fighting') { + if (user()->currentaction !== 'Fighting') { exit('Cheat attempt detected.

Get a life, loser.'); } @@ -41,7 +40,7 @@ class Fight $playerisdead = 0; // Generate spell list - $user_spells = Lib::user()->spells(); + $user_spells = user()->spells(); if (! empty($user_spells)) { $page['magiclist'] = ' '; + $page .= '
'; $page .= 'You may also choose to just continue exploring and give up this item.'; return $page; @@ -469,40 +468,40 @@ class Fight public static function handleMonsterTurn(&$userrow, $monsterrow) { $pagearray = ''; - if (Lib::user()->currentmonstersleep != 0) { + if (user()->currentmonstersleep != 0) { $chancetowake = rand(1, 15); - if ($chancetowake > Lib::user()->currentmonstersleep) { - Lib::user()->currentmonstersleep = 0; + if ($chancetowake > user()->currentmonstersleep) { + user()->currentmonstersleep = 0; $pagearray .= 'The monster has woken up.
'; } else { $pagearray .= 'The monster is still asleep.
'; } } - if (Lib::user()->currentmonstersleep == 0) { + if (user()->currentmonstersleep == 0) { $tohit = (int) ceil(mt_rand((int) ($monsterrow['maxdam'] * 0.5), (int) $monsterrow['maxdam'])); - $toblock = (int) ceil(mt_rand((int) (Lib::user()->defensepower * 0.75), (int) Lib::user()->defensepower) / 4); + $toblock = (int) ceil(mt_rand((int) (user()->defensepower * 0.75), (int) user()->defensepower) / 4); $tododge = rand(1, 150); - if ($tododge <= sqrt(Lib::user()->dexterity)) { + if ($tododge <= sqrt(user()->dexterity)) { $tohit = 0; $pagearray .= "You dodge the monster's attack. No damage has been scored.
"; $persondamage = 0; } else { $persondamage = max(1, $tohit - $toblock); - if (Lib::user()->currentuberdefense != 0) { - $persondamage -= (int) ceil($persondamage * (Lib::user()->currentuberdefense / 100)); + if (user()->currentuberdefense != 0) { + $persondamage -= (int) ceil($persondamage * (user()->currentuberdefense / 100)); } $persondamage = max(1, $persondamage); } $pagearray .= "The monster attacks you for $persondamage damage.

"; - Lib::user()->currenthp -= $persondamage; + user()->currenthp -= $persondamage; - if (Lib::user()->currenthp <= 0) { - $newgold = (int) ceil(Lib::user()->gold / 2); - $newhp = (int) ceil(Lib::user()->maxhp / 4); - Lib::db()->query("UPDATE users SET currenthp=?, currentaction='In Town', currentmonster=0, currentmonsterhp=0, currentmonstersleep=0, currentmonsterimmune=0, currentfight=0, latitude=0, longitude=0, gold=? WHERE id=?;", [ + if (user()->currenthp <= 0) { + $newgold = (int) ceil(user()->gold / 2); + $newhp = (int) ceil(user()->maxhp / 4); + db()->query("UPDATE users SET currenthp=?, currentaction='In Town', currentmonster=0, currentmonsterhp=0, currentmonstersleep=0, currentmonsterimmune=0, currentfight=0, latitude=0, longitude=0, gold=? WHERE id=?;", [ $newhp, $newgold, $userrow['id'], ]); self::dead(); @@ -517,38 +516,38 @@ class Fight $pagearray = ''; switch ($newspellrow['type']) { case 1: // Heal spell - $newhp = min(Lib::user()->currenthp + $newspellrow['attribute'], Lib::user()->maxhp); - Lib::user()->currenthp = $newhp; - Lib::user()->currentmp -= $newspellrow['mp']; + $newhp = min(user()->currenthp + $newspellrow['attribute'], user()->maxhp); + user()->currenthp = $newhp; + user()->currentmp -= $newspellrow['mp']; $pagearray = "You have cast the {$newspellrow['name']} spell, and gained {$newspellrow['attribute']} Hit Points.

"; break; case 2: // Hurt spell - if (Lib::user()->currentmonsterimmune == 0) { + if (user()->currentmonsterimmune == 0) { $monsterdamage = mt_rand((int) (($newspellrow['attribute'] / 6) * 5), $newspellrow['attribute']); - Lib::user()->currentmonsterhp -= $monsterdamage; + user()->currentmonsterhp -= $monsterdamage; $pagearray = "You have cast the {$newspellrow['name']} spell for $monsterdamage damage.

"; } else { $pagearray = "You have cast the {$newspellrow['name']} spell, but the monster is immune to it.

"; } - Lib::user()->currentmp -= $newspellrow['mp']; + user()->currentmp -= $newspellrow['mp']; break; case 3: // Sleep spell - if (Lib::user()->currentmonsterimmune != 2) { - Lib::user()->currentmonstersleep = $newspellrow['attribute']; + if (user()->currentmonsterimmune != 2) { + user()->currentmonstersleep = $newspellrow['attribute']; $pagearray = "You have cast the {$newspellrow['name']} spell. The monster is asleep.

"; } else { $pagearray = "You have cast the {$newspellrow['name']} spell, but the monster is immune to it.

"; } - Lib::user()->currentmp -= $newspellrow['mp']; + user()->currentmp -= $newspellrow['mp']; break; case 4: // +Damage spell - Lib::user()->currentuberdamage = $newspellrow['attribute']; - Lib::user()->currentmp -= $newspellrow['mp']; + user()->currentuberdamage = $newspellrow['attribute']; + user()->currentmp -= $newspellrow['mp']; $pagearray = "You have cast the {$newspellrow['name']} spell, and will gain {$newspellrow['attribute']}% damage until the end of this fight.

"; break; case 5: // +Defense spell - Lib::user()->currentuberdefense = $newspellrow['attribute']; - Lib::user()->currentmp -= $newspellrow['mp']; + user()->currentuberdefense = $newspellrow['attribute']; + user()->currentmp -= $newspellrow['mp']; $pagearray = "You have cast the {$newspellrow['name']} spell, and will gain {$newspellrow['attribute']}% defense until the end of this fight.

"; break; } diff --git a/src/DragonKnight/Actions/Forum.php b/src/DragonKnight/Actions/Forum.php index e76f372..80711a7 100644 --- a/src/DragonKnight/Actions/Forum.php +++ b/src/DragonKnight/Actions/Forum.php @@ -13,7 +13,6 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; use DragonKnight\Router; class Forum @@ -31,7 +30,7 @@ class Forum public static function donothing($start = 0) { - $query = Lib::db()->query('SELECT * FROM forum WHERE parent=0 ORDER BY newpostdate DESC LIMIT 20 OFFSET ?;', [20 * $start]); + $query = db()->query('SELECT * FROM forum WHERE parent=0 ORDER BY newpostdate DESC LIMIT 20 OFFSET ?;', [20 * $start]); $page = << @@ -68,45 +67,45 @@ class Forum $page .= ''; - Lib::page_title('Forum'); + page_title('Forum'); return $page; } public static function showthread($id, $start) { - $posts = Lib::db()->query('SELECT * FROM forum WHERE id=? OR parent=? ORDER BY id LIMIT 15 OFFSET ?;', [$id, $id, $start * 15]); - $title = Lib::db()->query('SELECT title FROM forum WHERE id=? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); + $posts = db()->query('SELECT * FROM forum WHERE id=? OR parent=? ORDER BY id LIMIT 15 OFFSET ?;', [$id, $id, $start * 15]); + $title = db()->query('SELECT title FROM forum WHERE id=? LIMIT 1;', [$id])->fetchArray(SQLITE3_ASSOC); $page = '
\n"; while ($row = $posts->fetchArray(SQLITE3_ASSOC)) { - $page .= '\n"; + $page .= '\n"; } $page .= '
Forum :: '.$title['title']."
'.$row['author'].'

'.Lib::pretty_date($row['postdate']).'
'.nl2br($row['content'])."
'.$row['author'].'

'.pretty_date($row['postdate']).'
'.nl2br($row['content'])."

'; $page .= "
Reply To This Thread:

'; - Lib::page_title('Forum: '.$title['title']); + page_title('Forum: '.$title['title']); return $page; } public static function reply() { - $form = Lib::validate($_POST, [ + $form = validate($_POST, [ 'title' => [], 'content' => [], ]); if (! $form['valid']) { - exit(Lib::ul_from_validate_errors($form['errors'])); + exit(ul_from_validate_errors($form['errors'])); } $form = $form['data']; - Lib::db()->query('INSERT INTO forum (author, title, content, parent) VALUES (?, ?, ?, ?);', [ - Lib::user()->username, $form['title'], $form['content'], $form['parent'], + db()->query('INSERT INTO forum (author, title, content, parent) VALUES (?, ?, ?, ?);', [ + user()->username, $form['title'], $form['content'], $form['parent'], ]); - Lib::db()->query('UPDATE forum SET newpostdate=CURRENT_TIMESTAMP, replies=replies + 1 WHERE id=?;', [$form['parent']]); + db()->query('UPDATE forum SET newpostdate=CURRENT_TIMESTAMP, replies=replies + 1 WHERE id=?;', [$form['parent']]); return self::showthread($form['parent'], 0); } @@ -114,23 +113,23 @@ class Forum public static function newthread() { if (isset($_POST['submit'])) { - $form = Lib::validate($_POST, [ + $form = validate($_POST, [ 'title' => ['length:2-30'], 'content' => [], ]); if (! $form['valid']) { - exit(Lib::ul_from_validate_errors($form['errors'])); + exit(ul_from_validate_errors($form['errors'])); } $form = $form['data']; - Lib::db()->query('INSERT INTO forum (author, title, content) VALUES (?, ?, ?);', [ - Lib::user()->username, $form['title'], $form['content'], + db()->query('INSERT INTO forum (author, title, content) VALUES (?, ?, ?);', [ + user()->username, $form['title'], $form['content'], ]); - Lib::redirect('/forum/thread/'.Lib::db()->lastInsertRowID().'/0'); + redirect('/forum/thread/'.db()->lastInsertRowID().'/0'); } - Lib::page_title('Form: New Thread'); + page_title('Form: New Thread'); return '
Make A New Post:

Title:


Message:


'; } diff --git a/src/DragonKnight/Actions/Heal.php b/src/DragonKnight/Actions/Heal.php index 1e13ef3..dff212a 100644 --- a/src/DragonKnight/Actions/Heal.php +++ b/src/DragonKnight/Actions/Heal.php @@ -13,14 +13,12 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; - class Heal { public static function healspells(int $id): string { - $user_spells = Lib::user()->spells(); - $spell = Lib::get_spell($id); + $user_spells = user()->spells(); + $spell = get_spell($id); $has_spell = false; foreach ($user_spells as $us) { if ($us['id'] === $id) { @@ -32,23 +30,23 @@ class Heal $page = 'You have not yet learned this spell. Please go back and try again.'; } elseif ($spell['type'] !== 1) { $page = 'This is not a healing spell. Please go back and try again.'; - } elseif (Lib::user()->currentmp < $spell['mp']) { + } elseif (user()->currentmp < $spell['mp']) { $page = 'You do not have enough Magic Points to cast this spell. Please go back and try again.'; - } elseif (Lib::user()->currentaction === 'Fighting') { + } elseif (user()->currentaction === 'Fighting') { $page = 'You cannot use the Quick Spells list during a fight. Please go back and select the Healing Spell you wish to use from the Spells box on the main fighting screen to continue.'; - } elseif (Lib::user()->currenthp == Lib::user()->maxhp) { + } elseif (user()->currenthp == user()->maxhp) { $page = 'Your HP is already full. You don\'t need to use a Healing spell now.'; } else { - $restored = Lib::user()->restore_hp($spell['attribute']); - Lib::user()->currentmp -= $spell['mp']; - Lib::user()->save(); + $restored = user()->restore_hp($spell['attribute']); + user()->currentmp -= $spell['mp']; + user()->save(); $page = <<exploring. HTML; } - Lib::page_title('Casting '.$spell['name']); + page_title('Casting '.$spell['name']); return $page; } diff --git a/src/DragonKnight/Actions/Help.php b/src/DragonKnight/Actions/Help.php index b1fc11e..b8cbcf8 100644 --- a/src/DragonKnight/Actions/Help.php +++ b/src/DragonKnight/Actions/Help.php @@ -13,7 +13,6 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; use DragonKnight\Router; class Help @@ -270,7 +269,7 @@ class Help TypeNameCostAttributeSpecial HTML; - $items = Lib::db()->query('SELECT * FROM items ORDER BY id;'); + $items = db()->query('SELECT * FROM items ORDER BY id;'); $item_types = [1 => ['weapon', 'Attack'], 2 => ['armor', 'Defense'], 3 => ['shield', 'Defense']]; while ($item = $items->fetchArray(SQLITE3_ASSOC)) { @@ -279,7 +278,7 @@ class Help if ($item['special'] !== 'X') { $special = explode(',', $item['special']); - $attr = Lib::special_to_string($special[0]); + $attr = special_to_string($special[0]); $stat = (($special[1] > 0) ? '+' : '').$special[1]; $bigspecial = "$attr $stat"; } else { @@ -299,12 +298,12 @@ class Help NameMonster LevelAttribute 1Attribute 2 HTML; - $drops = Lib::db()->query('SELECT * FROM drops ORDER BY id;'); + $drops = db()->query('SELECT * FROM drops ORDER BY id;'); while ($drop = $drops->fetchArray(SQLITE3_ASSOC)) { if ($drop['attribute1'] !== 'X') { $special = explode(',', $drop['attribute1']); - $attr = Lib::special_to_string($special[0]); + $attr = special_to_string($special[0]); $stat = (($special[1] > 0) ? '+' : '').$special[1]; $bigspecial1 = "$attr $stat"; } else { @@ -313,7 +312,7 @@ class Help if ($drop['attribute2'] !== 'X') { $special = explode(',', $drop['attribute2']); - $attr = Lib::special_to_string($special[0]); + $attr = special_to_string($special[0]); $stat = (($special[1] > 0) ? '+' : '').$special[1]; $bigspecial2 = "$attr $stat"; } else { @@ -335,7 +334,7 @@ class Help NameCostTypeAttribute HTML; - $spells = Lib::db()->query('SELECT * FROM spells ORDER BY id;'); + $spells = db()->query('SELECT * FROM spells ORDER BY id;'); $spell_types = ['None', 'Heal', 'Hurt', 'Sleep', '+Damage (%)', '+Defense (%)']; while ($spell = $spells->fetchArray(SQLITE3_ASSOC)) { $page .= <<NameMax HPMax DamageArmorLevelMax Exp.Max GoldImmunity HTML; - $monsters = Lib::db()->query('SELECT * FROM monsters ORDER BY id;'); + $monsters = db()->query('SELECT * FROM monsters ORDER BY id;'); $immunities = ['None', 'Hurt', 'Hurt & Sleep']; while ($m = $monsters->fetchArray(SQLITE3_ASSOC)) { @@ -383,7 +382,7 @@ class Help { $rows = []; - $levels = Lib::db()->query('SELECT * FROM levels ORDER BY id;'); + $levels = db()->query('SELECT * FROM levels ORDER BY id;'); while ($level = $levels->fetchArray(SQLITE3_ASSOC)) { $class_data = [1 => [], 2 => [], 3 => []]; @@ -405,7 +404,7 @@ class Help } $spells = []; - $spells_query = Lib::db()->query('SELECT * FROM spells ORDER BY id;'); + $spells_query = db()->query('SELECT * FROM spells ORDER BY id;'); while ($spell = $spells_query->fetchArray(SQLITE3_ASSOC)) { $spells[$spell['id']] = $spell; } @@ -508,7 +507,7 @@ class Help public static function display_help(string $content) { - return Lib::render('layouts/help', [ + return render('layouts/help', [ 'content' => $content, 'version' => VERSION, 'build' => BUILD, diff --git a/src/DragonKnight/Actions/Install.php b/src/DragonKnight/Actions/Install.php index c62d9ed..b2e8c09 100644 --- a/src/DragonKnight/Actions/Install.php +++ b/src/DragonKnight/Actions/Install.php @@ -13,9 +13,13 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; use DragonKnight\Router; +use function DragonKnight\db; +use function DragonKnight\ul_from_validate_errors; +use function DragonKnight\user; +use function DragonKnight\validate; + class Install { public static function register_routes(Router $r): Router @@ -56,20 +60,20 @@ class Install */ public static function second() { - if (! is_dir($path = getcwd().'/db')) { + if (! is_dir($path = getcwd().'/db')) { if (mkdir($path, 0777, true) === false) { - throw new \Exception('Failed to create database directory at '.$path.'. Please check permissions.'); - } + throw new \Exception('Failed to create database directory at '.$path.'. Please check permissions.'); + } } if (file_exists($path = getcwd().'/db/database.db')) { if (unlink($path) === false) { - throw new \Exception('Failed to delete existing database file at '.$path.'. Please check permissions.'); - } + throw new \Exception('Failed to delete existing database file at '.$path.'. Please check permissions.'); + } } $page = 'Dragon Knight InstallationDragon Knight Installation: Page Two

'; - $query = Lib::db()->exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec("INSERT INTO news (content) VALUES ('This is the first news post. Please use the admin control panel to add another one and make this one go away.');"); + $query = db()->exec("INSERT INTO news (content) VALUES ('This is the first news post. Please use the admin control panel to add another one and make this one go away.');"); $page .= self::table_status_msg($query === true, 'News', 'populate'); - $query = Lib::db()->exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<<exec(<< ['length:3-18', 'alpha-spaces'], 'email' => ['email'], 'confirm_email' => ['confirm'], @@ -723,11 +727,11 @@ class Install ]); if (! $form['valid']) { - exit(Lib::ul_from_validate_errors($form['errors'])); + exit(ul_from_validate_errors($form['errors'])); } $form = $form['data']; - if (Lib::db()->query( + if (db()->query( "INSERT INTO users (username, password, email, verify, charclass, authlevel) VALUES (?, ?, ?, 'g2g', ?, 1)", [$form['username'], password_hash($form['password'], PASSWORD_ARGON2ID), $form['email'], $form['charclass'] ?? null] ) === false) { @@ -788,7 +792,7 @@ class Install }; if ($condition === false) { - return "Error {$verb[1]} $table_name table. (".Lib::db()->lastErrorMsg().')
'; + return "Error {$verb[1]} $table_name table. (".db()->lastErrorMsg().')
'; } return "$table_name table {$verb[0]}.
"; diff --git a/src/DragonKnight/Actions/Towns.php b/src/DragonKnight/Actions/Towns.php index ffea7af..a9d4c4e 100644 --- a/src/DragonKnight/Actions/Towns.php +++ b/src/DragonKnight/Actions/Towns.php @@ -13,9 +13,19 @@ declare(strict_types=1); namespace DragonKnight\Actions; -use DragonKnight\Lib; use DragonKnight\Router; +use function DragonKnight\db; +use function DragonKnight\env; +use function DragonKnight\get_town_by_xy; +use function DragonKnight\get_item; +use function DragonKnight\get_town_by_id; +use function DragonKnight\is_post; +use function DragonKnight\page_title; +use function DragonKnight\redirect; +use function DragonKnight\render; +use function DragonKnight\user; + class Towns { public static function register_routes(Router $r): Router @@ -36,7 +46,7 @@ class Towns */ public static function town() { - $town = Lib::get_town_by_xy(Lib::user()->longitude, Lib::user()->latitude); + $town = get_town_by_xy(user()->longitude, user()->latitude); if ($town === false) { exit('There is an error with your user account, or with the town data. Please try again.'); } @@ -44,9 +54,9 @@ class Towns $page = ['news' => '', 'whos_online' => '']; // News box. Grab latest news entry and display it. Something a little more graceful coming soon maybe. - if (Lib::env('show_news')) { - $news = Lib::db()->query('SELECT * FROM news ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC); - $news_date = Lib::pretty_date($news['postdate']); + if (env('show_news')) { + $news = db()->query('SELECT * FROM news ORDER BY id DESC LIMIT 1;')->fetchArray(SQLITE3_ASSOC); + $news_date = pretty_date($news['postdate']); $news_content = nl2br($news['content']); $page['news'] = <<Latest News @@ -56,8 +66,8 @@ class Towns } // Who's Online. Currently just members. Guests maybe later. - if (Lib::env('show_online')) { - $onlinequery = Lib::db()->query(<<query(<<= datetime('now', '-600 seconds') @@ -75,13 +85,13 @@ class Towns $online_rows = implode(', ', $online_rows); $page['whos_online'] = <<Who's Online - There are $online_count Lib::user(s) online within the last 10 minutes: $online_rows + There are $online_count user(s) online within the last 10 minutes: $online_rows HTML; } - Lib::page_title($town['name']); + page_title($town['name']); - return Lib::render('towns', ['town' => $town, 'news' => $page['news'], 'whos_online' => $page['whos_online']]); + return render('towns', ['town' => $town, 'news' => $page['news'], 'whos_online' => $page['whos_online']]); } /** @@ -90,25 +100,25 @@ class Towns */ public static function inn() { - $town = Lib::get_town_by_xy(Lib::user()->longitude, Lib::user()->latitude); + $town = get_town_by_xy(user()->longitude, user()->latitude); if ($town === false) { exit('Cheat attempt detected.

Get a life, loser.'); } - if (Lib::user()->gold < $town['innprice']) { + if (user()->gold < $town['innprice']) { $page = <<
You may return to town, or use the direction buttons on the left to start exploring. HTML; - } elseif (Lib::is_post() && $_POST['rest']) { - Lib::user()->gold -= $town['innprice']; - Lib::user()->restore_points()->save(); + } elseif (is_post() && $_POST['rest']) { + user()->gold -= $town['innprice']; + user()->restore_points()->save(); $page = <<
You may return to town, or use the direction buttons on the left to start exploring. HTML; - } elseif (Lib::is_post() && ! $_POST['rest']) { - Lib::redirect('/'); + } elseif (is_post() && ! $_POST['rest']) { + redirect('/'); } else { $page = <<
@@ -120,7 +130,7 @@ class Towns HTML; } - Lib::page_title($town['name'].' Inn'); + page_title($town['name'].' Inn'); return $page; } @@ -132,7 +142,7 @@ class Towns */ public static function shop() { - $town = Lib::get_town_by_xy(Lib::user()->longitude, Lib::user()->latitude); + $town = get_town_by_xy(user()->longitude, user()->latitude); if ($town === false) { exit('Cheat attempt detected.

Get a life, loser.'); } @@ -144,7 +154,7 @@ class Towns HTML; - $items = Lib::db()->query('SELECT * FROM items WHERE id IN ('.$town['itemslist'].');'); + $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 .= ''; - if (Lib::user()->weaponid === $item['id'] || Lib::user()->armorid === $item['id'] || Lib::user()->shieldid === $item['id']) { + if (user()->weaponid === $item['id'] || user()->armorid === $item['id'] || user()->shieldid === $item['id']) { $page .= <<{$item['name']} @@ -175,7 +185,7 @@ class Towns If you've changed your mind, you may also return back to town. HTML; - Lib::page_title($town['name'].' Shop'); + page_title($town['name'].' Shop'); return $page; } @@ -185,15 +195,15 @@ class Towns */ public static function buy(int $id) { - $town = Lib::get_town_by_xy(Lib::user()->longitude, Lib::user()->latitude); + $town = get_town_by_xy(user()->longitude, user()->latitude); if ($town === false) { - Lib::redirect('/'); + redirect('/'); } if (! in_array($id, explode(',', $town['itemslist']))) { - Lib::redirect('/shop'); + redirect('/shop'); } - $item = Lib::get_item($id); - $can_afford = Lib::user()->gold >= $item['buycost']; + $item = get_item($id); + $can_afford = user()->gold >= $item['buycost']; if (! $can_afford) { $page = <<town, shop, or use the direction buttons on the left to start exploring. HTML; - } elseif (Lib::is_post() && ! $_POST['buy']) { - Lib::redirect('/shop'); - } elseif (Lib::is_post() && $_POST['buy']) { + } elseif (is_post() && ! $_POST['buy']) { + redirect('/shop'); + } elseif (is_post() && $_POST['buy']) { $type_mapping = [ 1 => ['id' => 'weaponid', 'name' => 'weaponname', 'power' => 'attackpower'], 2 => ['id' => 'armorid', 'name' => 'armorname', 'power' => 'defensepower'], @@ -215,9 +225,9 @@ class Towns } // Retrieve current equipped item or create a default - $current_equip_id = Lib::user()->{$type_mapping[$item['type']]['id']}; + $current_equip_id = user()->{$type_mapping[$item['type']]['id']}; if ($current_equip_id != 0) { - $item2 = Lib::get_item($current_equip_id); + $item2 = get_item($current_equip_id); } else { $item2 = ['attribute' => 0, 'buycost' => 0, 'special' => 'X']; } @@ -233,9 +243,9 @@ class Towns $toChange = $special[0]; $changeAmount = $index === 0 ? $special[1] : -$special[1]; - Lib::user()->$toChange += $changeAmount; + user()->$toChange += $changeAmount; $specialFields[] = "$toChange = ?"; - $specialValues[] = Lib::user()->$toChange; + $specialValues[] = user()->$toChange; // Adjust attack or defense power if ($toChange == 'strength' || $toChange == 'dexterity') { @@ -248,21 +258,21 @@ class Towns // Determine power and type-specific updates $currentType = $type_mapping[$item['type']]; $powerField = $currentType['power']; - Lib::user()->$powerField += $item['attribute'] - $item2['attribute']; + user()->$powerField += $item['attribute'] - $item2['attribute']; // Calculate new gold with trade-in value - Lib::user()->gold += ceil($item2['buycost'] / 2) - $item['buycost']; + user()->gold += ceil($item2['buycost'] / 2) - $item['buycost']; // Ensure current HP/MP/TP don't exceed max values - Lib::user()->currenthp = min(Lib::user()->currenthp, Lib::user()->maxhp); - Lib::user()->currentmp = min(Lib::user()->currentmp, Lib::user()->maxmp); - Lib::user()->currenttp = min(Lib::user()->currenttp, Lib::user()->maxtp); + user()->currenthp = min(user()->currenthp, user()->maxhp); + user()->currentmp = min(user()->currentmp, user()->maxmp); + user()->currenttp = min(user()->currenttp, user()->maxtp); // Update item info in user - Lib::user()->{$type_mapping[$item['type']]['id']} = $item['id']; - Lib::user()->{$type_mapping[$item['type']]['name']} = $item['name']; + user()->{$type_mapping[$item['type']]['id']} = $item['id']; + user()->{$type_mapping[$item['type']]['name']} = $item['name']; - Lib::user()->save(); + user()->save(); $page = <<{$item['name']}.

@@ -271,10 +281,10 @@ class Towns HTML; } else { $type_to_row_mapping = [1 => 'weaponid', 2 => 'armorid', 3 => 'shieldid']; - $current_equipped_id = Lib::user()->{$type_to_row_mapping[$item['type']]} ?? 0; + $current_equipped_id = user()->{$type_to_row_mapping[$item['type']]} ?? 0; if ($current_equipped_id != 0) { - $item2 = Lib::get_item($current_equipped_id); + $item2 = get_item($current_equipped_id); $sell_price = ceil($item2['buycost'] / 2); $page = <<
@@ -294,7 +304,7 @@ class Towns } } - Lib::page_title('Buying '.$item['name']); + page_title('Buying '.$item['name']); return $page; } @@ -310,8 +320,8 @@ class Towns
'; @@ -154,7 +164,7 @@ class Towns 3 => 'shield' }; $page .= ' $attrib {$item['attribute']}
HTML; - $mapped = explode(',', Lib::user()->towns); - $towns = Lib::db()->query('SELECT * FROM towns ORDER BY id;'); + $mapped = explode(',', user()->towns); + $towns = db()->query('SELECT * FROM towns ORDER BY id;'); while ($town = $towns->fetchArray(SQLITE3_ASSOC)) { $latitude = ($town['latitude'] >= 0) ? $town['latitude'].'N,' : ($town['latitude'] * -1).'S,'; $longitude = ($town['longitude'] >= 0) ? $town['longitude'].'E' : ($town['longitude'] * -1).'W'; @@ -341,34 +351,34 @@ class Towns If you've changed your mind, you may also return back to town. HTML; - Lib::page_title('Maps'); + page_title('Maps'); return $page; } public static function buy_map(int $id): string { - $town = Lib::get_town_by_id($id); + $town = get_town_by_id($id); if ($town === false) { - Lib::redirect('/maps'); + redirect('/maps'); } - if (Lib::user()->gold < $town['mapprice']) { + if (user()->gold < $town['mapprice']) { $page = <<
You may return to town, store, or use the direction buttons on the left to start exploring. HTML; - } elseif (Lib::is_post() && $_POST['buy']) { - Lib::user()->towns .= ",$id"; - Lib::user()->gold -= $town['mapprice']; - Lib::user()->save(); + } elseif (is_post() && $_POST['buy']) { + user()->towns .= ",$id"; + user()->gold -= $town['mapprice']; + user()->save(); $page = <<
You may return to town, map shop, or use the direction buttons on the left to start exploring. HTML; - } elseif (Lib::is_post() && ! $_POST['buy']) { - Lib::redirect('/maps'); + } elseif (is_post() && ! $_POST['buy']) { + redirect('/maps'); } else { $page = <<{$town['name']} map for {$town['mapprice']} gold. Is that ok?

@@ -379,7 +389,7 @@ class Towns HTML; } - Lib::page_title('Buying '.$town['name'].' Map'); + page_title('Buying '.$town['name'].' Map'); return $page; } @@ -389,24 +399,24 @@ class Towns */ public static function travelto(int $id, bool $use_points = true): string { - if (Lib::user()->currentaction == 'Fighting') { - Lib::redirect('/fight'); + if (user()->currentaction == 'Fighting') { + redirect('/fight'); } - $town = Lib::get_town_by_id($id); + $town = get_town_by_id($id); $cost = $use_points ? $town['travelpoints'] : 0; - $mapped = explode(',', Lib::user()->towns); + $mapped = explode(',', user()->towns); $travelled = false; if ($use_points && ! in_array($id, $mapped)) { // trying to teleport to this town when it is not mapped - Lib::redirect('/'); - } elseif (Lib::user()->currenttp < $cost) { + redirect('/'); + } elseif (user()->currenttp < $cost) { $page = 'You do not have enough TP to travel here. Please go back and try again when you get more TP.'; - } elseif ((Lib::user()->latitude == $town['latitude']) && (Lib::user()->longitude == $town['longitude'])) { + } elseif ((user()->latitude == $town['latitude']) && (user()->longitude == $town['longitude'])) { if (! in_array($id, $mapped)) { // add town to user's mapped if they travelled here - Lib::user()->towns .= ",$id"; + user()->towns .= ",$id"; $travelled = true; $page = <<{$town['name']}! It has been added to your mapped towns.

@@ -416,19 +426,19 @@ class Towns $page = 'You are already in this town. Click here to return.'; } } else { - Lib::user()->latitude = $town['latitude']; - Lib::user()->longitude = $town['longitude']; - Lib::user()->currenttp -= $cost; + user()->latitude = $town['latitude']; + user()->longitude = $town['longitude']; + user()->currenttp -= $cost; $travelled = true; $page = 'You have travelled to '.$town['name'].'. You may now enter this town.'; } if ($travelled) { - Lib::user()->currentaction = 'In Town'; - Lib::user()->save(); + user()->currentaction = 'In Town'; + user()->save(); } - Lib::page_title('Travelling to '.$town['name']); + page_title('Travelling to '.$town['name']); return $page; } diff --git a/src/DragonKnight/Auth.php b/src/DragonKnight/Auth.php index 2e8ce8b..95c1bb0 100644 --- a/src/DragonKnight/Auth.php +++ b/src/DragonKnight/Auth.php @@ -66,7 +66,7 @@ class Auth public function login(string $username, string $password): bool { - $user = Lib::get_user($username); + $user = get_user($username); if ($user === false) { return false; } diff --git a/src/DragonKnight/DragonKnight.php b/src/DragonKnight/DragonKnight.php new file mode 100644 index 0000000..2dab867 --- /dev/null +++ b/src/DragonKnight/DragonKnight.php @@ -0,0 +1,18 @@ +currentaction === 'In Town') { - $page = Towns::town(); - } elseif (self::user()->currentaction === 'Exploring') { - $page = Explore::explore(); - } elseif (self::user()->currentaction === 'Fighting') { - self::redirect('/fight'); - } - - return $page; - } - - /** - * Show the user their position on the current world map. Only works with a game size of 250 and the default towns 😅. - */ - public static function show_map() - { - $pos = sprintf( - '
', - round(258 + Lib::user()->longitude * (500 / 500) - 3), - round(258 - Lib::user()->latitude * (500 / 500) - 3) - ); - - echo Lib::render('layouts/minimal', [ - 'content' => 'Map'.$pos, - 'title' => 'Map', - ]); - } - - /** - * Show a character's info. Defaults to the currently logged in user. - */ - public static function show_character_info(int $id = 0): string - { - $user = $id !== 0 ? User::find($id) : Lib::user(); - if ($user === false) { - exit('Failed to show info for user ID '.$id); - } - - $level = Lib::db()->query("SELECT `{$user->charclass}_exp` FROM levels WHERE id=? LIMIT 1;", [$user->level + 1])->fetchArray(SQLITE3_ASSOC); - - $spells = $user->spells(); - $magic_list = 'None'; - if (! empty($spells)) { - $magic_list = ''; - foreach ($spells as $spell) { - $magic_list .= $spell['name'].'
'; - } - } - - $showchar = Lib::render('show_char', [ - 'char' => $user, - 'level' => $level, - 'magic_list' => $magic_list, - ]); - - return Lib::render('layouts/minimal', ['content' => $showchar, 'title' => $user->username.' Information']); - } - - /** - * Handle a POST request to send a new babblebox message. - */ - public static function babblebox() - { - if (Lib::is_post()) { - $content = trim($_POST['babble']); - if (! empty($content)) { - Lib::db()->query( - 'INSERT INTO babble (posttime, author, babble) VALUES (CURRENT_TIMESTAMP, ?, ?);', - [Lib::user()->username, $content] - ); - } - - return self::babblebox_messages(); - } - } - - /** - * The handler that is polled by HTMX for new babblebox messages. - */ - public static function babblebox_messages(): string - { - if (Lib::user() === false) { - return ''; - } - - $query = Lib::db()->query('SELECT * FROM babble ORDER BY id ASC LIMIT 40;'); - $has_chats = false; - $messages = ''; - while ($row = $query->fetchArray(SQLITE3_ASSOC)) { - $has_chats = true; - $messages .= '
['.$row['author'].'] '.Lib::make_safe($row['babble']).'
'; - } - if (! $has_chats) { - $messages = 'There are no messages. :('; - } - - return $messages; - } - - /** - * Open or get SQLite database connection. - */ - public static function db(): Database - { - if (! is_dir($path = getcwd().'/db')) { - error_log('Database folder not found at '.$path.'. Please run the installer first.'); - exit(); - } - return $GLOBALS['database'] ??= new Database(getcwd().'/db/database.db'); - } - - /** - * Redirect to a different URL, exit. - */ - public static function redirect(string $location): void - { - if (self::is_htmx()) { - $target = isset($_SERVER['HTTP_HX_TARGET']) ? '#'.$_SERVER['HTTP_HX_TARGET'] : '#middle'; - $json = json_encode(['path' => $location, 'target' => $target]); - header("HX-Location: $json"); - } else { - header("Location: $location"); - } - - exit; - } - - /** - * Render a view with the given data. Can be used redundantly within the template. - */ - public static function render(string $path_to_base_view, array $data = []): string|false - { - ob_start(); - extract($data); - require __DIR__."/../templates/$path_to_base_view.php"; - - return ob_get_clean(); - } - - /** - * Replace tags with given content. - */ - public static function parse(string $template, array $array): string - { - return strtr($template, array_combine( - array_map(fn ($key) => "{{{$key}}}", array_keys($array)), - array_values($array) - )); - } - - /** - * Change the SQLite3 datetime format (YYYY-MM-DD HH:MM:SS) into something friendlier. - */ - public static function pretty_date(string $uglydate): string - { - return date('l, F j, Y', mktime( - 0, - 0, - 0, - (int) substr($uglydate, 5, 2), // Month - (int) substr($uglydate, 8, 2), // Day - (int) substr($uglydate, 0, 4) // Year - )); - } - - /** - * Use htmlentities with UTF-8 encoding to ensure we're only outputting healthy, safe and effective HTML. - */ - public static function make_safe(string $content): string - { - return htmlentities($content, ENT_QUOTES, 'UTF-8'); - } - - /** - * Finalize admin page and output to browser. - */ - public static function display_admin($content, $title) - { - echo self::render('layouts/admin', [ - 'title' => $title, - 'content' => $content, - ]); - - exit; - } - - /** - * Determine what game skin to use. If a user is logged in then it uses their setting, otherwise defaults to 0 (retro). - */ - public static function game_skin(): int - { - return self::user() !== false ? self::user()->game_skin : 0; - } - - /** - * Get a town's data by it's coordinates. - */ - public static function get_town_by_xy(int $x, int $y): array|false - { - $cache_tag = "town-$x-$y"; - - if (! isset($GLOBALS['cache'][$cache_tag])) { - $query = self::db()->query('SELECT * FROM towns WHERE longitude = ? AND latitude = ? LIMIT 1;', [$x, $y]); - if ($query === false) { - return false; - } - $GLOBALS['cache'][$cache_tag] = $query->fetchArray(SQLITE3_ASSOC); - } - - return $GLOBALS['cache'][$cache_tag]; - } - - /** - * Get a town's data by it's ID. - */ - public static function get_town_by_id(int $id): array|false - { - $query = self::db()->query('SELECT * FROM towns WHERE id = ? LIMIT 1;', [$id]); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Get a user's data by their ID, username or email. - */ - public static function get_user(int|string $id, string $data = '*'): array|false - { - $query = self::db()->query( - "SELECT $data FROM users WHERE id=? OR username=? COLLATE NOCASE OR email=? COLLATE NOCASE LIMIT 1;", - [$id, $id, $id] - ); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Get an item by it's ID. - */ - public static function get_item(int $id): array|false - { - $query = self::db()->query('SELECT * FROM items WHERE id=? LIMIT 1;', [$id]); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Get a drop by it's ID. - */ - public static function get_drop(int $id): array|false - { - $query = self::db()->query('SELECT * FROM drops WHERE id=? LIMIT 1;', [$id]); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Get a spell by it's ID. - */ - public static function get_spell(int $id): array|false - { - $query = self::db()->query('SELECT * FROM spells WHERE id=? LIMIT 1;', [$id]); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Get a monster by it's ID. - */ - public static function get_monster(int $id): array|false - { - $query = self::db()->query('SELECT * FROM monsters WHERE id=? LIMIT 1;', [$id]); - if ($query === false) { - return false; - } - - return $query->fetchArray(SQLITE3_ASSOC); - } - - /** - * Translate a Specials keyword to it's string. - */ - public static function special_to_string(string $special): string - { - return match ($special) { - 'maxhp' => 'Max HP', - 'maxmp' => 'Max MP', - 'maxtp' => 'Max TP', - 'goldbonus' => 'Gold Bonus (%)', - 'expbonus' => 'Experience Bonus (%)', - 'strength' => 'Strength', - 'dexterity' => 'Dexterity', - 'attackpower' => 'Attack Power', - 'defensepower' => 'Defense Power', - default => $special - }; - } - - /** - * Generate a pretty dope token. - */ - public static function token($length = 32): string - { - return bin2hex(random_bytes($length)); - } - - /** - * Validate any given array of data against rules. Returns [valid, data, error]. Data contains the trimmed - * values from the input array. Note: all fields with rules are assumed to be required, unless the optional - * rule is used. - * - * Example: ['required', 'no-trim', 'length:5-20', 'alphanum-spaces'] - */ - public static function validate(array $input_data, array $rules): array - { - $data = []; - $errors = []; - - foreach ($rules as $field => $field_rules) { - $value = $input_data[$field] ?? null; - $field_name = ucfirst(str_replace('_', ' ', $field)); - $is_required = true; - $default_value = null; - - if (in_array('optional', $field_rules)) { - $is_required = false; - } - - foreach ($field_rules as $rule) { - if (strpos($rule, 'default:') === 0) { - $default_value = substr($rule, 8); - break; - } - } - - if (($value === null || $value === '') && $default_value !== null) { - $value = $default_value; - } - - if (($value === null || $value === '') && ! $is_required) { - continue; - } - - if ($is_required && ($value === null || $value === '')) { - $errors[$field][] = "{$field_name} is required."; - continue; - } - - if (! in_array('no-trim', $field_rules)) { - $value = trim($value); - } - - $data[$field] = $value; - - foreach ($field_rules as $rule) { - // Parse rule and arguments - if (strpos($rule, ':') !== false) { - list($rule_name, $rule_args) = explode(':', $rule, 2); - } else { - $rule_name = $rule; - $rule_args = null; - } - - if ($rule_name === 'optional') { - continue; - } - - switch ($rule_name) { - case 'bool': - if (! isset($input_data[$field]) || empty($value)) { - $value = false; - } else { - $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); - - if ($value === null) { - $errors[$field][] = "{$field_name} must be a valid boolean value."; - } - } - break; - - case 'length': - list($min, $max) = explode('-', $rule_args); - $len = strlen((string) $value); - if ($len < $min || $len > $max) { - $errors[$field][] = "{$field_name} must be between {$min} and {$max} characters."; - } - break; - - case 'alphanum': - if (! preg_match('/^[a-zA-Z0-9]+$/', $value)) { - $errors[$field][] = "{$field_name} must contain only letters and numbers."; - } - break; - - case 'alpha': - if (! preg_match('/^[a-zA-Z]+$/', $value)) { - $errors[$field][] = "{$field_name} must contain only letters and numbers."; - } - break; - - case 'alphanum-spaces': - if (! preg_match('/^[a-zA-Z0-9\s_]+$/', $value)) { - $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; - } - break; - - case 'alpha-spaces': - if (! preg_match('/^[a-zA-Z\s_]+$/', $value)) { - $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; - } - break; - - case 'email': - if (! filter_var($value, FILTER_VALIDATE_EMAIL)) { - $errors[$field][] = "{$field_name} must be a valid email address."; - } - break; - - case 'int': - if (filter_var($value, FILTER_VALIDATE_INT) === false) { - $errors[$field][] = "{$field_name} must be an integer."; - } - break; - - case 'min': - if ($value < $rule_args) { - $errors[$field][] = "{$field_name} must be at least {$rule_args}."; - } - break; - - case 'max': - if ($value > $rule_args) { - $errors[$field][] = "{$field_name} must be no more than {$rule_args}."; - } - break; - - case 'regex': - if (! preg_match($rule_args, $value)) { - $errors[$field][] = "{$field_name} does not match the required pattern."; - } - break; - - case 'in': - $options = explode(',', $rule_args); - if (! in_array($value, $options)) { - $errors[$field][] = "{$field_name} must be one of: ".implode(', ', $options); - } - break; - - case 'confirm': - $field_to_confirm = substr($field, 8); - $confirm_value = $data[$field_to_confirm] ?? ''; - $confirm_field_name = ucfirst(str_replace('_', ' ', $field_to_confirm)); - if ($value !== $confirm_value) { - $errors[$field][] = "{$field_name} must match {$confirm_field_name}."; - } - break; - - case 'unique': - list($table, $column) = explode(',', $rule_args, 2); - if (self::db()->exists($table, $column, $value)) { - $errors[$field][] = "{$field_name} must be unique."; - } - break; - } - } - } - - foreach ($input_data as $field => $value) { - if (! isset($data[$field])) { - $data[$field] = trim($value); - } - } - - return [ - 'valid' => empty($errors), - 'data' => $data, - 'errors' => $errors, - ]; - } - - /** - * Generates a ul list from `validate()`'s errors. - */ - public static function ul_from_validate_errors(array $errors): string - { - $string = '
    '; - foreach ($errors as $field => $errors) { - $string .= '
  • '; - foreach ($errors as $error) { - $string .= $error; - } - $string .= '
  • '; - } - - return $string.'
'; - } - - /** - * Load the environment variables from the .env file. - */ - public static function env_load(string $filePath): void - { - if (! file_exists($filePath)) { - throw new \Exception('The .env file does not exist. (el)'); - } - - $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - foreach ($lines as $line) { - $line = trim($line); - - // Skip lines that are empty after trimming or are comments - if ($line === '' || str_starts_with($line, '#')) { - continue; - } - - // Skip lines without an '=' character - if (strpos($line, '=') === false) { - continue; - } - - [$name, $value] = explode('=', $line, 2); - - $name = trim($name); - $value = trim($value, " \t\n\r\0\x0B\"'"); // Trim whitespace and quotes - - if (! array_key_exists($name, $_SERVER) && ! array_key_exists($name, $_ENV)) { - putenv("$name=$value"); - $_ENV[$name] = $value; - $_SERVER[$name] = $value; - } - } - } - - /** - * Retrieve an environment variable. - */ - public static function env(string $key, mixed $default = null): mixed - { - $v = $_ENV[$key] ?? $_SERVER[$key] ?? (getenv($key) ?: $default); - - return match (true) { - $v === 'true' => true, - $v === 'false' => false, - is_numeric($v) => (int) $v, - is_float($v) => (float) $v, - default => $v - }; - } - - /** - * Get the data on spells from a given list of IDs. - */ - public static 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 = self::db()->query("SELECT id, name, type FROM spells WHERE id IN($placeholders)", $spell_ids); - if ($query === false) { - return false; - } - $rows = []; - while ($row = $query->fetchArray(SQLITE3_ASSOC)) { - $rows[] = $row; - } - - return ! empty($rows) ? $rows : false; - } - - public static function generate_stat_bar(int $current, int $max): string - { - $percent = $max > 0 ? round(max(0, $current) / $max * 100, 4) : 0; - if ($percent < 0) { - $percent = 0; - } - if ($percent > 100) { - $percent = 100; - } - $color = $percent >= 66 ? 'green' : ($percent >= 33 ? 'yellow' : 'red'); - - return << -
- - HTML; - } - - public static function create_stat_table(): string - { - $stat_table = '
'. - '
'. - '
'.self::generate_stat_bar((int) self::user()->currenthp, (int) self::user()->maxhp).'
HP
'. - '
'.self::generate_stat_bar((int) self::user()->currentmp, (int) self::user()->maxmp).'
MP
'. - '
'.self::generate_stat_bar((int) self::user()->currenttp, (int) self::user()->maxtp).'
TP
'. - '
'. - '
'; - - return $stat_table; - } - - /** - * Returns the user in the GLOBALS state, if there is one. If not, populates it if there is a SESSION user_id. - */ - public static function user(): User|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. - */ - public static 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'; - } - - /** - * Return whether the request is POST. - */ - public static function is_post(): bool - { - return $_SERVER['REQUEST_METHOD'] === 'POST'; - } - - /** - * Get the current page title per updates. Optionally set a new title. - */ - public static function page_title(string $new_title = ''): string - { - if ($new_title) { - return $GLOBALS['state']['new-page-title'] = $new_title; - } - - return $GLOBALS['state']['new-page-title'] ?? self::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. - */ - public static function render_response(array $uri, string $content): string - { - if ($uri[0] === 'babblebox') { - return $content; - } - - if (self::is_htmx()) { - header('HX-Push-Url: '.$_SERVER['REQUEST_URI']); - - $content .= ''.self::page_title().''; - - $content .= Render::debug_db_info(); - - if (self::env('debug', false)) { - $content .= Render::debug_query_log(); - } - - if ($GLOBALS['state']['user-state-changed'] ?? false) { - $content .= Render::right_nav(); - $content .= Render::left_nav(); - } - } - - return Render::content($content, self::page_layout()); - } - - /** - * Get/set page layout through GLOBALS state. - */ - public static function page_layout(string $layout = ''): string - { - if ($layout === '') { - return $GLOBALS['state']['page-layout'] ?? 'layouts/primary'; - } - - return $GLOBALS['state']['page-layout'] = $layout; - } -} diff --git a/src/DragonKnight/Models/Model.php b/src/DragonKnight/Models/Model.php index cc02788..b52da04 100644 --- a/src/DragonKnight/Models/Model.php +++ b/src/DragonKnight/Models/Model.php @@ -13,8 +13,6 @@ declare(strict_types=1); namespace DragonKnight\Models; -use DragonKnight\Lib; - class Model { protected string $table_name = ''; @@ -59,7 +57,7 @@ class Model $values[] = $this->id; $query = 'UPDATE '.$this->table_name.' SET '.implode(', ', $placeholders).' WHERE id = ?;'; - $result = Lib::db()->query($query, $values); + $result = db()->query($query, $values); return $result === false ? false : true; } diff --git a/src/DragonKnight/Models/User.php b/src/DragonKnight/Models/User.php index f43a3f2..a5d4ba8 100644 --- a/src/DragonKnight/Models/User.php +++ b/src/DragonKnight/Models/User.php @@ -13,8 +13,6 @@ declare(strict_types=1); namespace DragonKnight\Models; -use DragonKnight\Lib; - class User extends Model { protected string $table_name = 'users'; @@ -24,7 +22,7 @@ class User extends Model */ public static function find(int|string $id): User|false { - $query = Lib::db()->query( + $query = db()->query( 'SELECT * FROM users WHERE id=? OR username=? COLLATE NOCASE OR email=? COLLATE NOCASE LIMIT 1;', [$id, $id, $id] ); @@ -44,7 +42,7 @@ class User extends Model */ public function spells(): array|false { - return Lib::get_spells_from_list($this->spells); + return get_spells_from_list($this->spells); } /** @@ -67,7 +65,7 @@ class User extends Model if ($this->onlinetime && strtotime($this->onlinetime) > strtotime('-9 minutes')) { return; } - Lib::db()->query('UPDATE users SET onlinetime=CURRENT_TIMESTAMP WHERE id=?;', [$this->id]); + db()->query('UPDATE users SET onlinetime=CURRENT_TIMESTAMP WHERE id=?;', [$this->id]); } /** @@ -105,7 +103,7 @@ class User extends Model $values[] = $this->id; $query = 'UPDATE '.$this->table_name.' SET '.implode(', ', $placeholders).' WHERE id = ?;'; - $result = Lib::db()->query($query, $values); + $result = db()->query($query, $values); if ($result === false) { return false; } diff --git a/src/DragonKnight/Render.php b/src/DragonKnight/Render.php index 4e08d91..5f0582a 100644 --- a/src/DragonKnight/Render.php +++ b/src/DragonKnight/Render.php @@ -21,37 +21,37 @@ class Render { /** * Prepare content for final render. If the request is HTMX-based, will return just the content passed to it. Otherwise - * it will Lib::render() onto $layout with some additional bits. + * it will render() onto $layout with some additional bits. */ public static function content(string $content, string $layout = 'layouts/primary'): string { - if (Lib::is_htmx()) { + if (is_htmx()) { return $content; } - return Lib::render($layout, ['content' => $content]); + return render($layout, ['content' => $content]); } public static function debug_db_info(): string { $total_time = round(microtime(true) - START, 4); - $htmx = Lib::is_htmx() ? ' (htmx)' : ''; + $htmx = is_htmx() ? ' (htmx)' : ''; - return '
'.$total_time.' Seconds, '.Lib::db()->count.' Queries'.$htmx.'
'; + return '
'.$total_time.' Seconds, '.db()->count.' Queries'.$htmx.'
'; } public static function right_nav(): string { - if (Lib::user() === false) { + if (user() === false) { return ''; } // Flashy numbers if they're low - $hp = (Lib::user()->currenthp <= (Lib::user()->maxhp / 5)) ? '*'.Lib::user()->currenthp.'*' : Lib::user()->currenthp; - $mp = (Lib::user()->currentmp <= (Lib::user()->maxmp / 5)) ? '*'.Lib::user()->currentmp.'*' : Lib::user()->currentmp; + $hp = (user()->currenthp <= (user()->maxhp / 5)) ? '*'.user()->currenthp.'*' : user()->currenthp; + $mp = (user()->currentmp <= (user()->maxmp / 5)) ? '*'.user()->currentmp.'*' : user()->currentmp; - $template = Lib::render('right_nav', ['hp' => $hp, 'mp' => $mp]); - if (Lib::is_htmx()) { + $template = render('right_nav', ['hp' => $hp, 'mp' => $mp]); + if (is_htmx()) { $template = ''; } @@ -60,12 +60,12 @@ class Render public static function left_nav(): string { - if (Lib::user() === false) { + if (user() === false) { return ''; } - $template = Lib::render('left_nav'); - if (Lib::is_htmx()) { + $template = render('left_nav'); + if (is_htmx()) { $template = '
'.$template.'
'; } @@ -74,13 +74,13 @@ class Render public static function babblebox(): string { - return Lib::render('babblebox', ['messages' => babblebox_messages()]); + return render('babblebox', ['messages' => babblebox_messages()]); } public static function debug_query_log(): string { $html = '
';
-        foreach (Lib::db()->log as $record) {
+        foreach (db()->log as $record) {
             $query_string = str_replace(["\r\n", "\n", "\r"], ' ', $record[0]);
             $error_string = ! empty($record[2]) ? '// '.$record[2] : '';
             $html .= '
['.round($record[1], 2)."s] {$query_string}{$error_string}
"; diff --git a/src/DragonKnight/functions.php b/src/DragonKnight/functions.php new file mode 100644 index 0000000..52242f0 --- /dev/null +++ b/src/DragonKnight/functions.php @@ -0,0 +1,724 @@ +currentaction === 'In Town') { + $page = Towns::town(); + } elseif (user()->currentaction === 'Exploring') { + $page = Explore::explore(); + } elseif (user()->currentaction === 'Fighting') { + redirect('/fight'); + } + + return $page; +} + +/** + * 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( + '
', + round(258 + user()->longitude * (500 / 500) - 3), + round(258 - user()->latitude * (500 / 500) - 3) + ); + + echo render('layouts/minimal', [ + 'content' => 'Map'.$pos, + 'title' => 'Map', + ]); +} + +/** + * Show a character's info. Defaults to the currently logged in user. + */ +function show_character_info(int $id = 0): string +{ + $user = $id !== 0 ? User::find($id) : user(); + if ($user === false) { + exit('Failed to show info for user ID '.$id); + } + + $level = db()->query("SELECT `{$user->charclass}_exp` FROM levels WHERE id=? LIMIT 1;", [$user->level + 1])->fetchArray(SQLITE3_ASSOC); + + $spells = $user->spells(); + $magic_list = 'None'; + if (! empty($spells)) { + $magic_list = ''; + foreach ($spells as $spell) { + $magic_list .= $spell['name'].'
'; + } + } + + $showchar = render('show_char', [ + 'char' => $user, + 'level' => $level, + 'magic_list' => $magic_list, + ]); + + return render('layouts/minimal', ['content' => $showchar, 'title' => $user->username.' Information']); +} + +/** + * Handle a POST request to send a new babblebox message. + */ +function babblebox() +{ + if (is_post()) { + $content = trim($_POST['babble']); + if (! empty($content)) { + db()->query( + 'INSERT INTO babble (posttime, author, babble) VALUES (CURRENT_TIMESTAMP, ?, ?);', + [user()->username, $content] + ); + } + + return babblebox_messages(); + } +} + +/** + * The handler that is polled by HTMX for new babblebox messages. + */ +function babblebox_messages(): string +{ + if (user() === false) { + return ''; + } + + $query = db()->query('SELECT * FROM babble ORDER BY id ASC LIMIT 40;'); + $has_chats = false; + $messages = ''; + while ($row = $query->fetchArray(SQLITE3_ASSOC)) { + $has_chats = true; + $messages .= '
['.$row['author'].'] '.make_safe($row['babble']).'
'; + } + if (! $has_chats) { + $messages = 'There are no messages. :('; + } + + return $messages; +} + +/** + * Open or get SQLite database connection. + */ +function db(): Database +{ + if (! is_dir($path = getcwd().'/db')) { + error_log('Database folder not found at '.$path.'. Please run the installer first.'); + exit(); + } + + return $GLOBALS['database'] ??= new Database(getcwd().'/db/database.db'); +} + +/** + * Redirect to a different URL, exit. + */ +function redirect(string $location): void +{ + if (is_htmx()) { + $target = isset($_SERVER['HTTP_HX_TARGET']) ? '#'.$_SERVER['HTTP_HX_TARGET'] : '#middle'; + $json = json_encode(['path' => $location, 'target' => $target]); + header("HX-Location: $json"); + } else { + header("Location: $location"); + } + + exit; +} + +/** + * Render a view with the given data. Can be used redundantly within the template. + */ +function render(string $path_to_base_view, array $data = []): string|false +{ + ob_start(); + extract($data); + require __DIR__."/../templates/$path_to_base_view.php"; + + return ob_get_clean(); +} + +/** + * Replace tags with given content. + */ +function parse(string $template, array $array): string +{ + return strtr($template, array_combine( + array_map(fn ($key) => "{{{$key}}}", array_keys($array)), + array_values($array) + )); +} + +/** + * Change the SQLite3 datetime format (YYYY-MM-DD HH:MM:SS) into something friendlier. + */ +function pretty_date(string $uglydate): string +{ + return date('l, F j, Y', mktime( + 0, + 0, + 0, + (int) substr($uglydate, 5, 2), // Month + (int) substr($uglydate, 8, 2), // Day + (int) substr($uglydate, 0, 4) // Year + )); +} + +/** + * Use htmlentities with UTF-8 encoding to ensure we're only outputting healthy, safe and effective HTML. + */ +function make_safe(string $content): string +{ + return htmlentities($content, ENT_QUOTES, 'UTF-8'); +} + +/** + * Finalize admin page and output to browser. + */ +function display_admin($content, $title) +{ + echo render('layouts/admin', [ + 'title' => $title, + 'content' => $content, + ]); + + exit; +} + +/** + * Determine what game skin to use. If a user is logged in then it uses their setting, otherwise defaults to 0 (retro). + */ +function game_skin(): int +{ + return user() !== false ? user()->game_skin : 0; +} + +/** + * Get a town's data by it's coordinates. + */ +function get_town_by_xy(int $x, int $y): array|false +{ + $cache_tag = "town-$x-$y"; + + if (! isset($GLOBALS['cache'][$cache_tag])) { + $query = db()->query('SELECT * FROM towns WHERE longitude = ? AND latitude = ? LIMIT 1;', [$x, $y]); + if ($query === false) { + return false; + } + $GLOBALS['cache'][$cache_tag] = $query->fetchArray(SQLITE3_ASSOC); + } + + return $GLOBALS['cache'][$cache_tag]; +} + +/** + * Get a town's data by it's ID. + */ +function get_town_by_id(int $id): array|false +{ + $query = db()->query('SELECT * FROM towns WHERE id = ? LIMIT 1;', [$id]); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Get a user's data by their ID, username or email. + */ +function get_user(int|string $id, string $data = '*'): array|false +{ + $query = db()->query( + "SELECT $data FROM users WHERE id=? OR username=? COLLATE NOCASE OR email=? COLLATE NOCASE LIMIT 1;", + [$id, $id, $id] + ); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Get an item by it's ID. + */ +function get_item(int $id): array|false +{ + $query = db()->query('SELECT * FROM items WHERE id=? LIMIT 1;', [$id]); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Get a drop by it's ID. + */ +function get_drop(int $id): array|false +{ + $query = db()->query('SELECT * FROM drops WHERE id=? LIMIT 1;', [$id]); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Get a spell by it's ID. + */ +function get_spell(int $id): array|false +{ + $query = db()->query('SELECT * FROM spells WHERE id=? LIMIT 1;', [$id]); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Get a monster by it's ID. + */ +function get_monster(int $id): array|false +{ + $query = db()->query('SELECT * FROM monsters WHERE id=? LIMIT 1;', [$id]); + if ($query === false) { + return false; + } + + return $query->fetchArray(SQLITE3_ASSOC); +} + +/** + * Translate a Specials keyword to it's string. + */ +function special_to_string(string $special): string +{ + return match ($special) { + 'maxhp' => 'Max HP', + 'maxmp' => 'Max MP', + 'maxtp' => 'Max TP', + 'goldbonus' => 'Gold Bonus (%)', + 'expbonus' => 'Experience Bonus (%)', + 'strength' => 'Strength', + 'dexterity' => 'Dexterity', + 'attackpower' => 'Attack Power', + 'defensepower' => 'Defense Power', + default => $special + }; +} + +/** + * Generate a pretty dope token. + */ +function token($length = 32): string +{ + return bin2hex(random_bytes($length)); +} + +/** + * Validate any given array of data against rules. Returns [valid, data, error]. Data contains the trimmed + * values from the input array. Note: all fields with rules are assumed to be required, unless the optional + * rule is used. + * + * Example: ['required', 'no-trim', 'length:5-20', 'alphanum-spaces'] + */ +function validate(array $input_data, array $rules): array +{ + $data = []; + $errors = []; + + foreach ($rules as $field => $field_rules) { + $value = $input_data[$field] ?? null; + $field_name = ucfirst(str_replace('_', ' ', $field)); + $is_required = true; + $default_value = null; + + if (in_array('optional', $field_rules)) { + $is_required = false; + } + + foreach ($field_rules as $rule) { + if (strpos($rule, 'default:') === 0) { + $default_value = substr($rule, 8); + break; + } + } + + if (($value === null || $value === '') && $default_value !== null) { + $value = $default_value; + } + + if (($value === null || $value === '') && ! $is_required) { + continue; + } + + if ($is_required && ($value === null || $value === '')) { + $errors[$field][] = "{$field_name} is required."; + continue; + } + + if (! in_array('no-trim', $field_rules)) { + $value = trim($value); + } + + $data[$field] = $value; + + foreach ($field_rules as $rule) { + // Parse rule and arguments + if (strpos($rule, ':') !== false) { + list($rule_name, $rule_args) = explode(':', $rule, 2); + } else { + $rule_name = $rule; + $rule_args = null; + } + + if ($rule_name === 'optional') { + continue; + } + + switch ($rule_name) { + case 'bool': + if (! isset($input_data[$field]) || empty($value)) { + $value = false; + } else { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + + if ($value === null) { + $errors[$field][] = "{$field_name} must be a valid boolean value."; + } + } + break; + + case 'length': + list($min, $max) = explode('-', $rule_args); + $len = strlen((string) $value); + if ($len < $min || $len > $max) { + $errors[$field][] = "{$field_name} must be between {$min} and {$max} characters."; + } + break; + + case 'alphanum': + if (! preg_match('/^[a-zA-Z0-9]+$/', $value)) { + $errors[$field][] = "{$field_name} must contain only letters and numbers."; + } + break; + + case 'alpha': + if (! preg_match('/^[a-zA-Z]+$/', $value)) { + $errors[$field][] = "{$field_name} must contain only letters and numbers."; + } + break; + + case 'alphanum-spaces': + if (! preg_match('/^[a-zA-Z0-9\s_]+$/', $value)) { + $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; + } + break; + + case 'alpha-spaces': + if (! preg_match('/^[a-zA-Z\s_]+$/', $value)) { + $errors[$field][] = "{$field_name} must contain only letters, numbers, spaces, and underscores."; + } + break; + + case 'email': + if (! filter_var($value, FILTER_VALIDATE_EMAIL)) { + $errors[$field][] = "{$field_name} must be a valid email address."; + } + break; + + case 'int': + if (filter_var($value, FILTER_VALIDATE_INT) === false) { + $errors[$field][] = "{$field_name} must be an integer."; + } + break; + + case 'min': + if ($value < $rule_args) { + $errors[$field][] = "{$field_name} must be at least {$rule_args}."; + } + break; + + case 'max': + if ($value > $rule_args) { + $errors[$field][] = "{$field_name} must be no more than {$rule_args}."; + } + break; + + case 'regex': + if (! preg_match($rule_args, $value)) { + $errors[$field][] = "{$field_name} does not match the required pattern."; + } + break; + + case 'in': + $options = explode(',', $rule_args); + if (! in_array($value, $options)) { + $errors[$field][] = "{$field_name} must be one of: ".implode(', ', $options); + } + break; + + case 'confirm': + $field_to_confirm = substr($field, 8); + $confirm_value = $data[$field_to_confirm] ?? ''; + $confirm_field_name = ucfirst(str_replace('_', ' ', $field_to_confirm)); + if ($value !== $confirm_value) { + $errors[$field][] = "{$field_name} must match {$confirm_field_name}."; + } + break; + + case 'unique': + list($table, $column) = explode(',', $rule_args, 2); + if (db()->exists($table, $column, $value)) { + $errors[$field][] = "{$field_name} must be unique."; + } + break; + } + } + } + + foreach ($input_data as $field => $value) { + if (! isset($data[$field])) { + $data[$field] = trim($value); + } + } + + return [ + 'valid' => empty($errors), + 'data' => $data, + 'errors' => $errors, + ]; +} + +/** + * Generates a ul list from `validate()`'s errors. + */ +function ul_from_validate_errors(array $errors): string +{ + $string = '
    '; + foreach ($errors as $field => $errors) { + $string .= '
  • '; + foreach ($errors as $error) { + $string .= $error; + } + $string .= '
  • '; + } + + return $string.'
'; +} + +/** + * Load the environment variables from the .env file. + */ +function env_load(string $filePath): void +{ + if (! file_exists($filePath)) { + throw new \Exception('The .env file does not exist. (el)'); + } + + $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + foreach ($lines as $line) { + $line = trim($line); + + // Skip lines that are empty after trimming or are comments + if ($line === '' || str_starts_with($line, '#')) { + continue; + } + + // Skip lines without an '=' character + if (strpos($line, '=') === false) { + continue; + } + + [$name, $value] = explode('=', $line, 2); + + $name = trim($name); + $value = trim($value, " \t\n\r\0\x0B\"'"); // Trim whitespace and quotes + + if (! array_key_exists($name, $_SERVER) && ! array_key_exists($name, $_ENV)) { + putenv("$name=$value"); + $_ENV[$name] = $value; + $_SERVER[$name] = $value; + } + } +} + +/** + * Retrieve an environment variable. + */ +function env(string $key, mixed $default = null): mixed +{ + $v = $_ENV[$key] ?? $_SERVER[$key] ?? (getenv($key) ?: $default); + + return match (true) { + $v === 'true' => true, + $v === 'false' => false, + is_numeric($v) => (int) $v, + is_float($v) => (float) $v, + default => $v + }; +} + +/** + * Get the data on spells from a given list of IDs. + */ +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; + } + $rows = []; + while ($row = $query->fetchArray(SQLITE3_ASSOC)) { + $rows[] = $row; + } + + return ! empty($rows) ? $rows : false; +} + +function generate_stat_bar(int $current, int $max): string +{ + $percent = $max > 0 ? round(max(0, $current) / $max * 100, 4) : 0; + if ($percent < 0) { + $percent = 0; + } + if ($percent > 100) { + $percent = 100; + } + $color = $percent >= 66 ? 'green' : ($percent >= 33 ? 'yellow' : 'red'); + + return << +
+ + HTML; +} + +function create_stat_table(): string +{ + $stat_table = '
'. + '
'. + '
'.generate_stat_bar((int) user()->currenthp, (int) user()->maxhp).'
HP
'. + '
'.generate_stat_bar((int) user()->currentmp, (int) user()->maxmp).'
MP
'. + '
'.generate_stat_bar((int) user()->currenttp, (int) user()->maxtp).'
TP
'. + '
'. + '
'; + + return $stat_table; +} + +/** + * Returns the user in the GLOBALS state, if there is one. If not, populates it if there is a SESSION user_id. + */ +function user(): User|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'; +} + +/** + * Return whether the request is POST. + */ +function is_post(): bool +{ + return $_SERVER['REQUEST_METHOD'] === 'POST'; +} + +/** + * Get the current page title per updates. Optionally set a new title. + */ +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 ($uri[0] === 'babblebox') { + return $content; + } + + if (is_htmx()) { + 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 Render::content($content, page_layout()); +} + +/** + * Get/set page layout through GLOBALS state. + */ +function page_layout(string $layout = ''): string +{ + if ($layout === '') { + return $GLOBALS['state']['page-layout'] ?? 'layouts/primary'; + } + + return $GLOBALS['state']['page-layout'] = $layout; +} diff --git a/templates/admin/edit_level.php b/templates/admin/edit_level.php index 22b63d5..34749ef 100644 --- a/templates/admin/edit_level.php +++ b/templates/admin/edit_level.php @@ -15,7 +15,7 @@ Experience values for each level should be the cumulative total amount of experi
- + diff --git a/templates/admin/edit_user.php b/templates/admin/edit_user.php index b920a87..87d0bd8 100644 --- a/templates/admin/edit_user.php +++ b/templates/admin/edit_user.php @@ -23,9 +23,9 @@ diff --git a/templates/admin/main_settings.php b/templates/admin/main_settings.php index 202ab88..cd67492 100644 --- a/templates/admin/main_settings.php +++ b/templates/admin/main_settings.php @@ -8,8 +8,8 @@ @@ -42,7 +42,7 @@ @@ -65,8 +65,8 @@ @@ -75,8 +75,8 @@ @@ -85,23 +85,23 @@ - + - + - +
EXP ">
HP ">
MP ">
Longitude
Character Class
Game Open:
Close the game if you are upgrading or working on settings and don't want to cause odd errors for end-users. Closing the game will completely halt all activity. @@ -18,14 +18,14 @@
Game Name: -
+
Change this if you want to change to call your game something different.
Game URL: -
+
Please specify the full URL to your game installation ("https://www.dragonknight.com/"). This gets used in the registration email sent to users. If you leave this field blank or incorrect, users may not be able to register correctly. @@ -34,7 +34,7 @@
Admin Email: -
+
Please specify your email address. This gets used when the game has to send an email to users.
Map Size: -
+
Default is 250. This is the size of each map quadrant. Note that monster levels increase every 5 spaces, so you should ensure that you have at least (map size / 5) @@ -55,8 +55,8 @@
Email Verification:
Make users verify their email address for added security.
Show News:
Toggle display of the Latest News box in towns.
Show Who's Online:
Toggle display of the Who's Online box in towns.
Show Babblebox:
Toggle display of the Babble Box in towns.
Class 1 Name:

Class 2 Name:

Class 3 Name:

diff --git a/templates/layouts/admin.php b/templates/layouts/admin.php index aaed0e2..f0480b7 100644 --- a/templates/layouts/admin.php +++ b/templates/layouts/admin.php @@ -3,14 +3,14 @@ - <?php echo DragonKnight\Lib::page_title() ?> + <?php echo DragonKnight\page_title() ?>
-

+

Admin

@@ -44,7 +44,7 @@
Version
-
diff --git a/templates/layouts/help.php b/templates/layouts/help.php index 3d79544..df18039 100644 --- a/templates/layouts/help.php +++ b/templates/layouts/help.php @@ -3,12 +3,12 @@ - <?php echo DragonKnight\Lib::env('game_name', 'Dragon Knight') ?> Help + <?php echo DragonKnight\env('game_name', 'Dragon Knight') ?> Help -

Help

+

Help

[ Back to Help ]
[ Return to Game ] diff --git a/templates/layouts/primary.php b/templates/layouts/primary.php index 54c1862..4c68c7c 100644 --- a/templates/layouts/primary.php +++ b/templates/layouts/primary.php @@ -3,7 +3,7 @@ - <?php echo DragonKnight\Lib::page_title() ?> + <?php echo DragonKnight\page_title() ?> @@ -20,12 +20,12 @@ } - +
- +
diff --git a/templates/left_nav.php b/templates/left_nav.php index 84012fe..5e958e2 100644 --- a/templates/left_nav.php +++ b/templates/left_nav.php @@ -1,9 +1,9 @@
Location
- Currently: currentaction ?>
+ Currently: currentaction ?>
latitude; - $lon = DragonKnight\Lib::user()->longitude; + $lat = DragonKnight\user()->latitude; + $lon = DragonKnight\user()->longitude; if ($lat < 0) { $lat = ($lat * -1).'S'; } else { @@ -31,15 +31,15 @@
Towns
currentaction == 'In Town') { - $town = DragonKnight\Lib::get_town_by_xy((int) DragonKnight\Lib::user()->latitude, (int) DragonKnight\Lib::user()->longitude); + if (DragonKnight\user()->currentaction == 'In Town') { + $town = DragonKnight\get_town_by_xy((int) DragonKnight\user()->latitude, (int) DragonKnight\user()->longitude); echo "Welcome to {$town['name']}.

"; } ?> Travel To:
towns); - $towns = DragonKnight\Lib::db()->query('SELECT * FROM towns ORDER BY id;'); + $town_list = explode(',', DragonKnight\user()->towns); + $towns = DragonKnight\db()->query('SELECT * FROM towns ORDER BY id;'); $mapped = false; while ($row = $towns->fetchArray(SQLITE3_ASSOC)) { $mapped = true; @@ -60,7 +60,7 @@ Home
Forum
Settings
- authlevel === 1): ?> + authlevel === 1): ?> Admin
Help
diff --git a/templates/register.php b/templates/register.php index 5a981a0..987f667 100644 --- a/templates/register.php +++ b/templates/register.php @@ -9,9 +9,9 @@ Character Class: diff --git a/templates/right_nav.php b/templates/right_nav.php index 571ee1d..c8ec59d 100644 --- a/templates/right_nav.php +++ b/templates/right_nav.php @@ -1,30 +1,30 @@
Character
- username ?>
- Level: level ?>
- Exp: experience) ?>
- Gold: gold) ?>
+ username ?>
+ Level: level ?>
+ Exp: experience) ?>
+ Gold: gold) ?>
HP:
MP:
- TP: currenttp ?>

-
+ TP: currenttp ?>

+
Extended Stats
Inventory
- Weapon weaponname ?>
- Armor armorname ?>
- Shield shieldname ?>
- Slot 1: slot1name ?>
- Slot 2: slot2name ?>
- Slot 3: slot3name ?> + Weapon weaponname ?>
+ Armor armorname ?>
+ Shield shieldname ?>
+ Slot 1: slot1name ?>
+ Slot 2: slot2name ?>
+ Slot 3: slot3name ?>
Fast Spells
spells(); + $user_spells = DragonKnight\user()->spells(); if ($user_spells !== false) { foreach ($user_spells as $spell) { // list only healing spells for now diff --git a/templates/show_char.php b/templates/show_char.php index 6b3a2eb..cb8acbd 100644 --- a/templates/show_char.php +++ b/templates/show_char.php @@ -2,9 +2,9 @@ username ?>

Class: charclass) { - 1 => DragonKnight\Lib::env('class_1_name'), - 2 => DragonKnight\Lib::env('class_2_name'), - 3 => DragonKnight\Lib::env('class_3_name') + 1 => DragonKnight\env('class_1_name'), + 2 => DragonKnight\env('class_2_name'), + 3 => DragonKnight\env('class_3_name') }; ?>

Level: level ?>