forked from Sky/Dragon-Knight
functions.php
This commit is contained in:
parent
feba005335
commit
4d5c345528
@ -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"
|
||||
}
|
||||
|
@ -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'] ?? []));
|
||||
|
@ -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 <<<HTML
|
||||
Welcome to the administration section. Use the links on the left bar to control and edit various
|
||||
@ -81,8 +95,8 @@ class Admin
|
||||
*/
|
||||
public static function primary(): string
|
||||
{
|
||||
if (Lib::is_post()) {
|
||||
$form = Lib::validate($_POST, [
|
||||
if (is_post()) {
|
||||
$form = validate($_POST, [
|
||||
'gamename' => ['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 = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
@ -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 = "<h2>Edit Items</h2>Click an item's name or ID to edit it.<br><br>\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 = "<h2>Edit Drops</h2>Click an item's name to edit it.<br><br>\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 = "<h2>Edit Towns</h2>Click an town's name or ID to edit it.<br><br>\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 = '<h2>Edit Monsters</h2>';
|
||||
|
||||
$page .= ((Lib::env('game_size') / 5) !== $max_level)
|
||||
? '<span class="highlight">Note:</span> 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.<br>"
|
||||
$page .= ((env('game_size') / 5) !== $max_level)
|
||||
? '<span class="highlight">Note:</span> 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.<br>"
|
||||
: 'Monster level and map size match. No further actions are required for map compatibility.<br>';
|
||||
|
||||
$page .= "Click an monster's name or ID to edit it.<br><br>\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 = "<h2>Edit Spells</h2>Click an spell's name to edit it.<br><br>\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 = <<<HTML
|
||||
<h2>Edit Levels</h2>
|
||||
@ -340,7 +354,7 @@ class Admin
|
||||
</form>
|
||||
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 <b>'.$id.'</b> 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 = '<h2>Edit Users</h2>Click a username or ID to edit the account.<br><br><div class="table-wrapper">';
|
||||
$page .= self::build_bulk_table($users, 'username', '/admin/users');
|
||||
|
||||
Lib::page_title('Admin: Users');
|
||||
page_title('Admin: Users');
|
||||
|
||||
return $page.'</div>';
|
||||
}
|
||||
@ -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 <b>'.$user['username'].'</b> updated.';
|
||||
} else {
|
||||
$error_list = Lib::ul_from_validate_errors($form['errors']);
|
||||
$error_list = ul_from_validate_errors($form['errors']);
|
||||
$page = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
@ -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('<br>', $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[] = '<th>'.
|
||||
Lib::make_safe($column === 'id' ? 'ID' : ucfirst($column)).
|
||||
make_safe($column === 'id' ? 'ID' : ucfirst($column)).
|
||||
'</th>';
|
||||
}
|
||||
$html_parts[] = '</tr></thead><tbody>';
|
||||
@ -551,7 +565,7 @@ class Admin
|
||||
foreach ($data as $row) {
|
||||
$html_parts[] = '<tr>';
|
||||
foreach ($columns as $column) {
|
||||
$name = Lib::make_safe($row[$column]);
|
||||
$name = make_safe($row[$column]);
|
||||
$html_parts[] = isset($is_edit_column[$column])
|
||||
? "<td><a href=\"{$edit_link}/{$row['id']}\" hx-get=\"{$edit_link}/{$row['id']}\" hx-target=\"#main\">{$name}</a></td>"
|
||||
: "<td>{$name}</td>";
|
||||
@ -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 ?: '<b>'.$form['data']['name'].'</b> updated.';
|
||||
} else {
|
||||
$error_list = Lib::ul_from_validate_errors($form['errors']);
|
||||
$error_list = ul_from_validate_errors($form['errors']);
|
||||
$page = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
|
@ -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 <<<HTML
|
||||
<div class="title"><img src="/img/title_exploring.gif" alt="Exploring"></div>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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.<br><br>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'] = '<select name="userspell">';
|
||||
foreach ($user_spells as $spell) {
|
||||
@ -51,52 +50,52 @@ class Fight
|
||||
}
|
||||
|
||||
// Determine initial combat parameters
|
||||
$chancetoswingfirst = rand(1, 10) + (int) ceil(sqrt(Lib::user()->dexterity));
|
||||
if (Lib::user()->currentfight === 1) {
|
||||
$maxlevel = (int) floor(max(abs(Lib::user()->latitude) + 5, abs(Lib::user()->longitude) + 5) / 5);
|
||||
$chancetoswingfirst = rand(1, 10) + (int) ceil(sqrt(user()->dexterity));
|
||||
if (user()->currentfight === 1) {
|
||||
$maxlevel = (int) floor(max(abs(user()->latitude) + 5, abs(user()->longitude) + 5) / 5);
|
||||
$minlevel = max(1, $maxlevel - 2);
|
||||
|
||||
$monster = Lib::db()->query('SELECT * FROM monsters WHERE level >= ? AND level <= ? ORDER BY RANDOM() LIMIT 1;', [
|
||||
$monster = db()->query('SELECT * FROM monsters WHERE level >= ? AND level <= ? ORDER BY RANDOM() LIMIT 1;', [
|
||||
$minlevel, $maxlevel,
|
||||
])->fetchArray(SQLITE3_ASSOC);
|
||||
|
||||
Lib::user()->currentmonster = $monster['id'];
|
||||
Lib::user()->currentmonsterhp = rand((int) (($monster['maxhp'] / 5) * 4), $monster['maxhp']);
|
||||
Lib::user()->currentmonstersleep = 0;
|
||||
Lib::user()->currentmonsterimmune = $monster['immune'];
|
||||
user()->currentmonster = $monster['id'];
|
||||
user()->currentmonsterhp = rand((int) (($monster['maxhp'] / 5) * 4), $monster['maxhp']);
|
||||
user()->currentmonstersleep = 0;
|
||||
user()->currentmonsterimmune = $monster['immune'];
|
||||
|
||||
$chancetoswingfirst = ($chancetoswingfirst > (rand(1, 7) + (int) ceil(sqrt($monster['maxdam'])))) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Get monster statistics
|
||||
$monster = Lib::get_monster(Lib::user()->currentmonster);
|
||||
$monster = get_monster(user()->currentmonster);
|
||||
$page['monstername'] = $monster['name'];
|
||||
|
||||
// Run action
|
||||
if (isset($_POST['run'])) {
|
||||
$chancetorun = rand(4, 10) + (int) ceil(sqrt(Lib::user()->dexterity));
|
||||
$chancetorun = rand(4, 10) + (int) ceil(sqrt(user()->dexterity));
|
||||
if ($chancetorun <= (rand(1, 5) + (int) ceil(sqrt($monster['maxdam'])))) {
|
||||
$page['yourturn'] = 'You tried to run away, but were blocked in front!<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".Lib::user()->currentmonsterhp.'<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".user()->currentmonsterhp.'<br><br>';
|
||||
|
||||
// Monster turn logic (similar to original public static function)
|
||||
$page['monsterturn'] = self::handleMonsterTurn($userrow, $monster);
|
||||
|
||||
Lib::user()->currentaction = 'Exploring';
|
||||
Lib::user()->save();
|
||||
Lib::redirect('/');
|
||||
user()->currentaction = 'Exploring';
|
||||
user()->save();
|
||||
redirect('/');
|
||||
}
|
||||
}
|
||||
|
||||
// Fight action
|
||||
if (isset($_POST['fight'])) {
|
||||
// Player's attack
|
||||
$min = (int) (Lib::user()->attackpower * 0.75);
|
||||
$max = (int) (Lib::user()->attackpower / 3);
|
||||
$min = (int) (user()->attackpower * 0.75);
|
||||
$max = (int) (user()->attackpower / 3);
|
||||
$tohit = (int) ceil(mt_rand(min($min, $max), max($min, $max)));
|
||||
|
||||
$toexcellent = rand(1, 150);
|
||||
if ($toexcellent <= sqrt(Lib::user()->strength)) {
|
||||
if ($toexcellent <= sqrt(user()->strength)) {
|
||||
$tohit *= 2;
|
||||
$page['yourturn'] .= 'Excellent hit!<br>';
|
||||
}
|
||||
@ -113,19 +112,19 @@ class Fight
|
||||
$page['yourturn'] .= 'The monster is dodging. No damage has been scored.<br>';
|
||||
}
|
||||
|
||||
if (Lib::user()->currentuberdamage != 0) {
|
||||
$monsterdamage += (int) ceil($monsterdamage * (Lib::user()->currentuberdamage / 100));
|
||||
if (user()->currentuberdamage != 0) {
|
||||
$monsterdamage += (int) ceil($monsterdamage * (user()->currentuberdamage / 100));
|
||||
}
|
||||
|
||||
Lib::user()->currentmonsterhp -= $monsterdamage;
|
||||
user()->currentmonsterhp -= $monsterdamage;
|
||||
$page['yourturn'] .= "You attack the monster for $monsterdamage damage.<br><br>";
|
||||
$page['monsterhp'] = "Monster's HP: ".Lib::user()->currentmonsterhp.'<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".user()->currentmonsterhp.'<br><br>';
|
||||
|
||||
// Check for monster defeat
|
||||
if (Lib::user()->currentmonsterhp <= 0) {
|
||||
Lib::user()->currentmonsterhp = 0;
|
||||
Lib::user()->save();
|
||||
Lib::redirect('/victory');
|
||||
if (user()->currentmonsterhp <= 0) {
|
||||
user()->currentmonsterhp = 0;
|
||||
user()->save();
|
||||
redirect('/victory');
|
||||
}
|
||||
|
||||
// Monster's turn
|
||||
@ -139,26 +138,26 @@ class Fight
|
||||
return 'You must select a spell first. Please go back and try again.';
|
||||
}
|
||||
|
||||
$newspellrow = Lib::get_spell($pickedspell);
|
||||
$spell = in_array($pickedspell, explode(',', Lib::user()->spells));
|
||||
$newspellrow = get_spell($pickedspell);
|
||||
$spell = in_array($pickedspell, explode(',', user()->spells));
|
||||
|
||||
if (! $spell) {
|
||||
return 'You have not yet learned this spell. Please go back and try again.';
|
||||
}
|
||||
|
||||
if (Lib::user()->currentmp < $newspellrow['mp']) {
|
||||
if (user()->currentmp < $newspellrow['mp']) {
|
||||
return 'You do not have enough Magic Points to cast this spell. Please go back and try again.';
|
||||
}
|
||||
|
||||
// Spell type handling (similar to original public static function)
|
||||
$page['yourturn'] = self::handleSpellCast($userrow, $newspellrow);
|
||||
$page['monsterhp'] = "Monster's HP: ".Lib::user()->currentmonsterhp.'<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".user()->currentmonsterhp.'<br><br>';
|
||||
|
||||
// Check for monster defeat
|
||||
if (Lib::user()->currentmonsterhp <= 0) {
|
||||
Lib::user()->currentmonsterhp = 0;
|
||||
Lib::user()->save();
|
||||
Lib::redirect('/victory');
|
||||
if (user()->currentmonsterhp <= 0) {
|
||||
user()->currentmonsterhp = 0;
|
||||
user()->save();
|
||||
redirect('/victory');
|
||||
}
|
||||
|
||||
// Monster's turn
|
||||
@ -168,7 +167,7 @@ class Fight
|
||||
// Monster's turn if player lost first swing
|
||||
if (! isset($_POST['run']) && ! isset($_POST['fight']) && ! isset($_POST['spell']) && $chancetoswingfirst == 0) {
|
||||
$page['yourturn'] = 'The monster attacks before you are ready!<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".Lib::user()->currentmonsterhp.'<br><br>';
|
||||
$page['monsterhp'] = "Monster's HP: ".user()->currentmonsterhp.'<br><br>';
|
||||
$page['monsterturn'] = self::handleMonsterTurn($userrow, $monster);
|
||||
}
|
||||
|
||||
@ -183,7 +182,7 @@ class Fight
|
||||
</form>
|
||||
HTML;
|
||||
|
||||
Lib::user()->currentfight += 1;
|
||||
user()->currentfight += 1;
|
||||
} else {
|
||||
$page['command'] = <<<HTML
|
||||
<b>You have died.</b><br><br>
|
||||
@ -192,24 +191,24 @@ class Fight
|
||||
HTML;
|
||||
}
|
||||
|
||||
Lib::user()->save();
|
||||
user()->save();
|
||||
|
||||
// Finalize page and display it
|
||||
$page = Lib::render('fight', ['page' => $page]);
|
||||
$page = render('fight', ['page' => $page]);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public static function victory()
|
||||
{
|
||||
if (Lib::user()->currentmonsterhp != 0) {
|
||||
Lib::redirect('/fight');
|
||||
if (user()->currentmonsterhp != 0) {
|
||||
redirect('/fight');
|
||||
}
|
||||
if (Lib::user()->currentfight == 0) {
|
||||
Lib::redirect('/');
|
||||
if (user()->currentfight == 0) {
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
$monsterrow = Lib::get_monster(Lib::user()->currentmonster);
|
||||
$monsterrow = get_monster(user()->currentmonster);
|
||||
|
||||
$min = (int) (($monsterrow['maxexp'] / 6) * 5);
|
||||
$max = (int) $monsterrow['maxexp'];
|
||||
@ -218,8 +217,8 @@ class Fight
|
||||
$exp = 1;
|
||||
}
|
||||
|
||||
if (Lib::user()->expbonus != 0) {
|
||||
$exp += ceil((Lib::user()->expbonus / 100) * $exp);
|
||||
if (user()->expbonus != 0) {
|
||||
$exp += ceil((user()->expbonus / 100) * $exp);
|
||||
}
|
||||
|
||||
$min = (int) (($monsterrow['maxgold'] / 6) * 5);
|
||||
@ -230,56 +229,56 @@ class Fight
|
||||
$gold = 1;
|
||||
}
|
||||
|
||||
if (Lib::user()->goldbonus != 0) {
|
||||
$gold += ceil((Lib::user()->goldbonus / 100) * $exp);
|
||||
if (user()->goldbonus != 0) {
|
||||
$gold += ceil((user()->goldbonus / 100) * $exp);
|
||||
}
|
||||
if (Lib::user()->experience + $exp < 16777215) {
|
||||
$newexp = Lib::user()->experience += $exp;
|
||||
if (user()->experience + $exp < 16777215) {
|
||||
$newexp = user()->experience += $exp;
|
||||
$warnexp = '';
|
||||
} else {
|
||||
$newexp = Lib::user()->experience;
|
||||
$newexp = user()->experience;
|
||||
$exp = 0;
|
||||
$warnexp = 'You have maxed out your experience points.';
|
||||
}
|
||||
if (Lib::user()->gold + $gold < 16777215) {
|
||||
$newgold = Lib::user()->gold += $gold;
|
||||
if (user()->gold + $gold < 16777215) {
|
||||
$newgold = user()->gold += $gold;
|
||||
$warngold = '';
|
||||
} else {
|
||||
$newgold = Lib::user()->gold;
|
||||
$newgold = user()->gold;
|
||||
$gold = 0;
|
||||
$warngold = 'You have maxed out your gold.';
|
||||
}
|
||||
|
||||
$levelrow = Lib::db()->query('SELECT * FROM levels WHERE id=? LIMIT 1;', [Lib::user()->level + 1])->fetchArray(SQLITE3_ASSOC);
|
||||
$levelrow = db()->query('SELECT * FROM levels WHERE id=? LIMIT 1;', [user()->level + 1])->fetchArray(SQLITE3_ASSOC);
|
||||
|
||||
if (Lib::user()->level < 100) {
|
||||
if ($newexp >= $levelrow[Lib::user()->charclass.'_exp']) {
|
||||
Lib::user()->maxhp += $levelrow[Lib::user()->charclass.'_hp'];
|
||||
Lib::user()->maxmp += $levelrow[Lib::user()->charclass.'_mp'];
|
||||
Lib::user()->maxtp += $levelrow[Lib::user()->charclass.'_tp'];
|
||||
Lib::user()->strength += $levelrow[Lib::user()->charclass.'_strength'];
|
||||
Lib::user()->dexterity += $levelrow[Lib::user()->charclass.'_dexterity'];
|
||||
Lib::user()->attackpower += $levelrow[Lib::user()->charclass.'_strength'];
|
||||
Lib::user()->defensepower += $levelrow[Lib::user()->charclass.'_dexterity'];
|
||||
Lib::user()->level += 1;
|
||||
if (user()->level < 100) {
|
||||
if ($newexp >= $levelrow[user()->charclass.'_exp']) {
|
||||
user()->maxhp += $levelrow[user()->charclass.'_hp'];
|
||||
user()->maxmp += $levelrow[user()->charclass.'_mp'];
|
||||
user()->maxtp += $levelrow[user()->charclass.'_tp'];
|
||||
user()->strength += $levelrow[user()->charclass.'_strength'];
|
||||
user()->dexterity += $levelrow[user()->charclass.'_dexterity'];
|
||||
user()->attackpower += $levelrow[user()->charclass.'_strength'];
|
||||
user()->defensepower += $levelrow[user()->charclass.'_dexterity'];
|
||||
user()->level += 1;
|
||||
$newlevel = $levelrow['id'];
|
||||
|
||||
if ($levelrow[Lib::user()->charclass.'_spells'] != 0) {
|
||||
Lib::user()->spells .= ','.$levelrow[Lib::user()->charclass.'_spells'];
|
||||
if ($levelrow[user()->charclass.'_spells'] != 0) {
|
||||
user()->spells .= ','.$levelrow[user()->charclass.'_spells'];
|
||||
$spelltext = 'You have learned a new spell.<br>';
|
||||
} else {
|
||||
$spelltext = '';
|
||||
$newspell = '';
|
||||
}
|
||||
|
||||
$page = 'Congratulations. You have defeated the '.$monsterrow['name'].".<br>You gain $exp experience. $warnexp <br>You gain $gold gold. $warngold <br><br><b>You have gained a level!</b><br><br>You gain ".$levelrow[Lib::user()->charclass.'_hp'].' hit points.<br>You gain '.$levelrow[Lib::user()->charclass.'_mp'].' magic points.<br>You gain '.$levelrow[Lib::user()->charclass.'_tp'].' travel points.<br>You gain '.$levelrow[Lib::user()->charclass.'_strength'].' strength.<br>You gain '.$levelrow[Lib::user()->charclass.'_dexterity']." dexterity.<br>$spelltext<br>You can now continue <a href=\"/\" hx-get=\"/\" hx-target=\"#middle\">exploring</a>.";
|
||||
$page = 'Congratulations. You have defeated the '.$monsterrow['name'].".<br>You gain $exp experience. $warnexp <br>You gain $gold gold. $warngold <br><br><b>You have gained a level!</b><br><br>You gain ".$levelrow[user()->charclass.'_hp'].' hit points.<br>You gain '.$levelrow[user()->charclass.'_mp'].' magic points.<br>You gain '.$levelrow[user()->charclass.'_tp'].' travel points.<br>You gain '.$levelrow[user()->charclass.'_strength'].' strength.<br>You gain '.$levelrow[user()->charclass.'_dexterity']." dexterity.<br>$spelltext<br>You can now continue <a href=\"/\" hx-get=\"/\" hx-target=\"#middle\">exploring</a>.";
|
||||
$title = 'Courage and Wit have served thee well!';
|
||||
$dropcode = '';
|
||||
} else {
|
||||
$page = 'Congratulations. You have defeated the '.$monsterrow['name'].".<br>You gain $exp experience. $warnexp <br>You gain $gold gold. $warngold <br><br>";
|
||||
|
||||
if (rand(1, 30) === 1) {
|
||||
$droprow = Lib::db()->query('SELECT * FROM drops WHERE mlevel <= ? ORDER BY RANDOM() LIMIT 1;', [$monsterrow['level']])->fetchArray(SQLITE3_ASSOC);
|
||||
$droprow = db()->query('SELECT * FROM drops WHERE mlevel <= ? ORDER BY RANDOM() LIMIT 1;', [$monsterrow['level']])->fetchArray(SQLITE3_ASSOC);
|
||||
$dropcode = "dropcode='".$droprow['id']."',";
|
||||
$page .= 'This monster has dropped an item. <a href="/drop" hx-get="/drop" hx-target="#middle">Click here</a> to reveal and equip the item, or you may also move on and continue <a href="/" hx-get="/" hx-target="#middle">exploring</a>.';
|
||||
} else {
|
||||
@ -291,26 +290,26 @@ class Fight
|
||||
}
|
||||
}
|
||||
|
||||
Lib::user()->currentaction = 'Exploring';
|
||||
Lib::user()->currentfight = 0;
|
||||
Lib::user()->currentuberdamage = 0;
|
||||
Lib::user()->currentuberdefense = 0;
|
||||
Lib::user()->currentmonstersleep = 0;
|
||||
Lib::user()->currentmonsterimmune = 0;
|
||||
Lib::user()->save();
|
||||
user()->currentaction = 'Exploring';
|
||||
user()->currentfight = 0;
|
||||
user()->currentuberdamage = 0;
|
||||
user()->currentuberdefense = 0;
|
||||
user()->currentmonstersleep = 0;
|
||||
user()->currentmonsterimmune = 0;
|
||||
user()->save();
|
||||
|
||||
Lib::page_title($title);
|
||||
page_title($title);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
public static function drop()
|
||||
{
|
||||
if (Lib::user()->dropcode == 0) {
|
||||
Lib::redirect('/');
|
||||
if (user()->dropcode == 0) {
|
||||
redirect('/');
|
||||
}
|
||||
|
||||
$droprow = Lib::get_drop(Lib::user()->dropcode);
|
||||
$droprow = get_drop(user()->dropcode);
|
||||
|
||||
if (isset($_POST['submit'])) {
|
||||
$slot = $_POST['slot'];
|
||||
@ -320,8 +319,8 @@ class Fight
|
||||
}
|
||||
|
||||
$slotstr = 'slot'.$slot.'id';
|
||||
if (Lib::user()->$slotstr != 0) {
|
||||
$slotrow = Lib::get_drop(Lib::user()->$slotstr);
|
||||
if (user()->$slotstr != 0) {
|
||||
$slotrow = get_drop(user()->$slotstr);
|
||||
|
||||
$old1 = explode(',', $slotrow['attribute1']);
|
||||
if ($slotrow['attribute2'] != 'X') {
|
||||
@ -336,52 +335,52 @@ class Fight
|
||||
$new2 = [0 => 'maxhp',1 => 0];
|
||||
}
|
||||
|
||||
Lib::user()->$old1[0] -= $old1[1];
|
||||
Lib::user()->$old2[0] -= $old2[1];
|
||||
user()->$old1[0] -= $old1[1];
|
||||
user()->$old2[0] -= $old2[1];
|
||||
if ($old1[0] == 'strength') {
|
||||
Lib::user()->attackpower -= $old1[1];
|
||||
user()->attackpower -= $old1[1];
|
||||
}
|
||||
if ($old1[0] == 'dexterity') {
|
||||
Lib::user()->defensepower -= $old1[1];
|
||||
user()->defensepower -= $old1[1];
|
||||
}
|
||||
if ($old2[0] == 'strength') {
|
||||
Lib::user()->attackpower -= $old2[1];
|
||||
user()->attackpower -= $old2[1];
|
||||
}
|
||||
if ($old2[0] == 'dexterity') {
|
||||
Lib::user()->defensepower -= $old2[1];
|
||||
user()->defensepower -= $old2[1];
|
||||
}
|
||||
|
||||
Lib::user()->$new1[0] += $new1[1];
|
||||
Lib::user()->$new2[0] += $new2[1];
|
||||
user()->$new1[0] += $new1[1];
|
||||
user()->$new2[0] += $new2[1];
|
||||
if ($new1[0] == 'strength') {
|
||||
Lib::user()->attackpower += $new1[1];
|
||||
user()->attackpower += $new1[1];
|
||||
}
|
||||
if ($new1[0] == 'dexterity') {
|
||||
Lib::user()->defensepower += $new1[1];
|
||||
user()->defensepower += $new1[1];
|
||||
}
|
||||
if ($new2[0] == 'strength') {
|
||||
Lib::user()->attackpower += $new2[1];
|
||||
user()->attackpower += $new2[1];
|
||||
}
|
||||
if ($new2[0] == 'dexterity') {
|
||||
Lib::user()->defensepower += $new2[1];
|
||||
user()->defensepower += $new2[1];
|
||||
}
|
||||
|
||||
if (Lib::user()->currenthp > Lib::user()->maxhp) {
|
||||
Lib::user()->currenthp = Lib::user()->maxhp;
|
||||
if (user()->currenthp > user()->maxhp) {
|
||||
user()->currenthp = user()->maxhp;
|
||||
}
|
||||
if (Lib::user()->currentmp > Lib::user()->maxmp) {
|
||||
Lib::user()->currentmp = Lib::user()->maxmp;
|
||||
if (user()->currentmp > user()->maxmp) {
|
||||
user()->currentmp = user()->maxmp;
|
||||
}
|
||||
if (Lib::user()->currenttp > Lib::user()->maxtp) {
|
||||
Lib::user()->currenttp = Lib::user()->maxtp;
|
||||
if (user()->currenttp > user()->maxtp) {
|
||||
user()->currenttp = user()->maxtp;
|
||||
}
|
||||
|
||||
$slot_s = 'slot'.$_POST['slot'];
|
||||
$slot_name = "{$slot_s}name";
|
||||
$slot_id = "{$slot_s}id";
|
||||
|
||||
Lib::user()->$slot_name = $droprow['name'];
|
||||
Lib::user()->$slot_id = $droprow['id'];
|
||||
user()->$slot_name = $droprow['name'];
|
||||
user()->$slot_id = $droprow['id'];
|
||||
} else {
|
||||
$new1 = explode(',', $droprow['attribute1']);
|
||||
if ($droprow['attribute2'] != 'X') {
|
||||
@ -390,30 +389,30 @@ class Fight
|
||||
$new2 = [0 => 'maxhp',1 => 0];
|
||||
}
|
||||
|
||||
Lib::user()->$new1[0] += $new1[1];
|
||||
Lib::user()->$new2[0] += $new2[1];
|
||||
user()->$new1[0] += $new1[1];
|
||||
user()->$new2[0] += $new2[1];
|
||||
if ($new1[0] == 'strength') {
|
||||
Lib::user()->attackpower += $new1[1];
|
||||
user()->attackpower += $new1[1];
|
||||
}
|
||||
if ($new1[0] == 'dexterity') {
|
||||
Lib::user()->defensepower += $new1[1];
|
||||
user()->defensepower += $new1[1];
|
||||
}
|
||||
if ($new2[0] == 'strength') {
|
||||
Lib::user()->attackpower += $new2[1];
|
||||
user()->attackpower += $new2[1];
|
||||
}
|
||||
if ($new2[0] == 'dexterity') {
|
||||
Lib::user()->defensepower += $new2[1];
|
||||
user()->defensepower += $new2[1];
|
||||
}
|
||||
|
||||
$slot_s = 'slot'.$_POST['slot'];
|
||||
$slot_name = "{$slot_s}name";
|
||||
$slot_id = "{$slot_s}id";
|
||||
|
||||
Lib::user()->$slot_name = $droprow['name'];
|
||||
Lib::user()->$slot_id = $droprow['id'];
|
||||
user()->$slot_name = $droprow['name'];
|
||||
user()->$slot_id = $droprow['id'];
|
||||
}
|
||||
|
||||
Lib::user()->save();
|
||||
user()->save();
|
||||
|
||||
return 'The item has been equipped. You can now continue <a href="/" hx-get="/" hx-target="#middle">exploring</a>.';
|
||||
}
|
||||
@ -450,7 +449,7 @@ class Fight
|
||||
}
|
||||
|
||||
$page .= '<br>Select an inventory slot from the list below to equip this item. If the inventory slot is already full, the old item will be discarded.';
|
||||
$page .= '<form action="/drop" method="post"><select name="slot"><option value="0">Choose One</option><option value="1">Slot 1: '.Lib::user()->slot1name.'</option><option value="2">Slot 2: '.Lib::user()->slot2name.'</option><option value="3">Slot 3: '.Lib::user()->slot3name.'</option></select> <input type="submit" name="submit" value="Submit" /></form>';
|
||||
$page .= '<form action="/drop" method="post"><select name="slot"><option value="0">Choose One</option><option value="1">Slot 1: '.user()->slot1name.'</option><option value="2">Slot 2: '.user()->slot2name.'</option><option value="3">Slot 3: '.user()->slot3name.'</option></select> <input type="submit" name="submit" value="Submit" /></form>';
|
||||
$page .= 'You may also choose to just continue <a href="/" hx-get="/" hx-target="#middle">exploring</a> 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.<br>';
|
||||
} else {
|
||||
$pagearray .= 'The monster is still asleep.<br>';
|
||||
}
|
||||
}
|
||||
|
||||
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.<br>";
|
||||
$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.<br><br>";
|
||||
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.<br><br>";
|
||||
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.<br><br>";
|
||||
} else {
|
||||
$pagearray = "You have cast the {$newspellrow['name']} spell, but the monster is immune to it.<br><br>";
|
||||
}
|
||||
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.<br><br>";
|
||||
} else {
|
||||
$pagearray = "You have cast the {$newspellrow['name']} spell, but the monster is immune to it.<br><br>";
|
||||
}
|
||||
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.<br><br>";
|
||||
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.<br><br>";
|
||||
break;
|
||||
}
|
||||
|
@ -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 = <<<HTML
|
||||
<table width="100%">
|
||||
<tr>
|
||||
@ -68,45 +67,45 @@ class Forum
|
||||
|
||||
$page .= '</table></td></tr></table>';
|
||||
|
||||
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 = '<table width="100%"><tr><td style="padding:1px; background-color:black;"><table width="100%" style="margins:0px;" cellspacing="1" cellpadding="3"><tr><td colspan="2" style="background-color:#dddddd;"><b><a href="/forum" hx-get="/forum" hx-target="#middle">Forum</a> :: '.$title['title']."</b></td></tr>\n";
|
||||
while ($row = $posts->fetchArray(SQLITE3_ASSOC)) {
|
||||
$page .= '<tr><td width="25%" style="background-color:#ffffff; vertical-align:top;"><span class="small"><b>'.$row['author'].'</b><br><br>'.Lib::pretty_date($row['postdate']).'</td><td style="background-color:#ffffff; vertical-align:top;">'.nl2br($row['content'])."</td></tr>\n";
|
||||
$page .= '<tr><td width="25%" style="background-color:#ffffff; vertical-align:top;"><span class="small"><b>'.$row['author'].'</b><br><br>'.pretty_date($row['postdate']).'</td><td style="background-color:#ffffff; vertical-align:top;">'.nl2br($row['content'])."</td></tr>\n";
|
||||
}
|
||||
$page .= '</table></td></tr></table><br>';
|
||||
$page .= "<table width=\"100%\"><tr><td><b>Reply To This Thread:</b><br><form action=\"/forum/reply\" method=\"post\" hx-post=\"/forum/reply\" hx-target=\"#middle\"><input type=\"hidden\" name=\"parent\" value=\"$id\" /><input type=\"hidden\" name=\"title\" value=\"Re: ".$title['title'].'" /><textarea name="content" rows="7" cols="40"></textarea><br><input type="submit" name="submit" value="Submit" /> <input type="reset" name="reset" value="Reset" /></form></td></tr></table>';
|
||||
|
||||
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 '<table width="100%"><tr><td><b>Make A New Post:</b><br><br/ ><form action="/forum/new" method="post" hx-post="/forum/new" hx-target="#middle">Title:<br><input type="text" name="title" size="50" maxlength="50" /><br><br>Message:<br><textarea name="content" rows="7" cols="40"></textarea><br><br><input type="submit" name="submit" value="Submit" /> <input type="reset" name="reset" value="Reset" /></form></td></tr></table>';
|
||||
}
|
||||
|
@ -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 = <<<HTML
|
||||
You have cast the {$spell['name']} spell, and gained {$restored} HP. You can now continue <a href="/" hx-get="/" hx-target="#middle">exploring</a>.
|
||||
HTML;
|
||||
}
|
||||
|
||||
Lib::page_title('Casting '.$spell['name']);
|
||||
page_title('Casting '.$spell['name']);
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
@ -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
|
||||
<tr><td><b>Type</b></td><td><b>Name</b></td><td><b>Cost</b></td><td><b>Attribute</b></td><td><b>Special</b></td></tr>
|
||||
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
|
||||
<tr><td><b>Name</b></td><td><b>Monster Level</b></td><td><b>Attribute 1</b></td><td><b>Attribute 2</b></td></tr>
|
||||
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
|
||||
<tr><td><b>Name</b></td><td><b>Cost</b></td><td><b>Type</b></td><td><b>Attribute</b></td></tr>
|
||||
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 .= <<<HTML
|
||||
@ -368,7 +367,7 @@ class Help
|
||||
<tr><td><b>Name</b></td><td><b>Max HP</b></td><td><b>Max Damage</b></td><td><b>Armor</b></td><td><b>Level</b></td><td><b>Max Exp.</b></td><td><b>Max Gold</b></td><td><b>Immunity</b></td></tr>
|
||||
HTML;
|
||||
|
||||
$monsters = Lib::db()->query('SELECT * FROM monsters ORDER BY id;');
|
||||
$monsters = db()->query('SELECT * FROM monsters ORDER BY id;');
|
||||
$immunities = ['<span class="light">None</span>', '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,
|
||||
|
@ -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 = '<html><head><title>Dragon Knight Installation</title></head><body><b>Dragon Knight Installation: Page Two</b><br><br>';
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE babble (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`posttime` TEXT NOT NULL DEFAULT '00:00:00',
|
||||
@ -80,7 +84,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Babble', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE drops (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT NOT NULL DEFAULT '',
|
||||
@ -93,7 +97,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Drops', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO drops VALUES
|
||||
(1, 'Life Pebble', 1, 1, 'maxhp,10', 'X'),
|
||||
(2, 'Life Stone', 10, 1, 'maxhp,25', 'X'),
|
||||
@ -131,7 +135,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Drops', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE forum (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`postdate` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
@ -146,7 +150,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Forum', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE items (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`type` INTEGER NOT NULL DEFAULT 0,
|
||||
@ -159,7 +163,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Items', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO items VALUES
|
||||
(1, 1, 'Stick', 10, 2, 'X'),
|
||||
(2, 1, 'Branch', 30, 4, 'X'),
|
||||
@ -198,7 +202,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Drops', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE classes (
|
||||
'id' INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
'name' TEXT NOT NULL,
|
||||
@ -217,7 +221,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Classes', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO classes VALUES
|
||||
(0, 'Adventurer', '', 3, 15, 10, 4, 4, 2, 2, 2, 2),
|
||||
(1, 'Mage', '', 1, 10, 15, 1, 7, 1, 3, 1, 2),
|
||||
@ -227,7 +231,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Classes', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE levels (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`1_exp` INTEGER NOT NULL DEFAULT 0,
|
||||
@ -256,7 +260,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Levels', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO levels VALUES
|
||||
(1, 0, 15, 0, 5, 5, 5, 0, 0, 15, 0, 5, 5, 5, 0, 0, 15, 0, 5, 5, 5, 0),
|
||||
(2, 15, 2, 5, 1, 0, 1, 1, 18, 2, 4, 1, 2, 1, 1, 20, 2, 5, 1, 0, 2, 1),
|
||||
@ -362,7 +366,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Levels', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE monsters (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT NOT NULL DEFAULT '',
|
||||
@ -378,7 +382,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Monsters', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO monsters VALUES
|
||||
(1, 'Blue Slime', 4, 3, 1, 1, 1, 1, 0),
|
||||
(2, 'Red Slime', 6, 5, 1, 1, 2, 1, 0),
|
||||
@ -535,7 +539,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Monsters', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE news (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`author` TEXT NOT NULL DEFAULT 'Guild Master',
|
||||
@ -546,11 +550,11 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'News', 'create');
|
||||
|
||||
$query = Lib::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.');");
|
||||
$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(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE spells (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT NOT NULL,
|
||||
@ -562,7 +566,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Spells', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO spells VALUES
|
||||
(1, 'Heal', 5, 10, 1),
|
||||
(2, 'Revive', 10, 25, 1),
|
||||
@ -587,7 +591,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Spells', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE towns (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT NOT NULL,
|
||||
@ -602,7 +606,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Towns', 'create');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
INSERT INTO towns VALUES
|
||||
(1, 'Midworld', 0, 0, 5, 0, 0, '1,2,3,17,18,19,28,29'),
|
||||
(2, 'Roma', 30, 30, 10, 25, 5, '2,3,4,18,19,29'),
|
||||
@ -616,7 +620,7 @@ class Install
|
||||
|
||||
$page .= self::table_status_msg($query === true, 'Towns', 'populate');
|
||||
|
||||
$query = Lib::db()->exec(<<<SQL
|
||||
$query = db()->exec(<<<SQL
|
||||
CREATE TABLE users (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`username` TEXT NOT NULL,
|
||||
@ -714,7 +718,7 @@ class Install
|
||||
public static function fourth(?array $post = null)
|
||||
{
|
||||
$post ??= $_POST;
|
||||
$form = Lib::validate($post, [
|
||||
$form = validate($post, [
|
||||
'username' => ['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().')<br>';
|
||||
return "Error {$verb[1]} $table_name table. (".db()->lastErrorMsg().')<br>';
|
||||
}
|
||||
|
||||
return "$table_name table {$verb[0]}.<br>";
|
||||
|
@ -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'] = <<<HTML
|
||||
<div class="title">Latest News</div>
|
||||
@ -56,8 +66,8 @@ class Towns
|
||||
}
|
||||
|
||||
// Who's Online. Currently just members. Guests maybe later.
|
||||
if (Lib::env('show_online')) {
|
||||
$onlinequery = Lib::db()->query(<<<SQL
|
||||
if (env('show_online')) {
|
||||
$onlinequery = db()->query(<<<SQL
|
||||
SELECT id, username
|
||||
FROM users
|
||||
WHERE onlinetime >= datetime('now', '-600 seconds')
|
||||
@ -75,13 +85,13 @@ class Towns
|
||||
$online_rows = implode(', ', $online_rows);
|
||||
$page['whos_online'] = <<<HTML
|
||||
<div class="title">Who's Online</div>
|
||||
There are <b>$online_count</b> Lib::user(s) online within the last 10 minutes: $online_rows
|
||||
There are <b>$online_count</b> 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.<br><br>Get a life, loser.');
|
||||
}
|
||||
|
||||
if (Lib::user()->gold < $town['innprice']) {
|
||||
if (user()->gold < $town['innprice']) {
|
||||
$page = <<<HTML
|
||||
You do not have enough gold to stay at this Inn tonight. <br><br>
|
||||
You may return to <a hx-get="/" hx-target="#middle">town</a>, or use the direction buttons on the left to start exploring.
|
||||
HTML;
|
||||
} elseif (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 = <<<HTML
|
||||
You wake up feeling refreshed and ready for action. <br><br>
|
||||
You may return to <a hx-get="/" hx-target="#middle">town</a>, or use the direction buttons on the left to start exploring.
|
||||
HTML;
|
||||
} elseif (Lib::is_post() && ! $_POST['rest']) {
|
||||
Lib::redirect('/');
|
||||
} elseif (is_post() && ! $_POST['rest']) {
|
||||
redirect('/');
|
||||
} else {
|
||||
$page = <<<HTML
|
||||
Resting at the inn will refill your current HP, MP, and TP to their maximum levels.<br><br>
|
||||
@ -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.<br><br>Get a life, loser.');
|
||||
}
|
||||
@ -144,7 +154,7 @@ class Towns
|
||||
<table>
|
||||
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 .= '<tr><td width="4%">';
|
||||
@ -154,7 +164,7 @@ class Towns
|
||||
3 => '<img src="/img/icon_shield.gif" alt="shield">'
|
||||
};
|
||||
$page .= '</td>';
|
||||
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 .= <<<HTML
|
||||
<td width="32%"><span class="light">{$item['name']}</span></td>
|
||||
<td width="32%"><span class="light">$attrib {$item['attribute']}</span></td>
|
||||
@ -175,7 +185,7 @@ class Towns
|
||||
If you've changed your mind, you may also return back to <a hx-get="/" hx-target="#middle">town</a>.
|
||||
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 = <<<HTML
|
||||
@ -201,9 +211,9 @@ class Towns
|
||||
You may return to <a hx-get="/" hx-target="#middle">town</a>, <a hx-get="/shop" hx-target="#middle">shop</a>,
|
||||
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 = <<<HTML
|
||||
Thank you for purchasing <b>{$item['name']}</b>.<br><br>
|
||||
@ -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 = <<<HTML
|
||||
If you are buying the {$item['name']}, then I will buy your {$item2['name']} for $sell_price gold. Is that ok?<br><br>
|
||||
@ -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
|
||||
<table>
|
||||
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 <a hx-get="/" hx-target="#middle">town</a>.
|
||||
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 = <<<HTML
|
||||
You do not have enough gold to buy this map.<br><br>
|
||||
You may return to <a hx-get="/" hx-target="#middle">town</a>, <a hx-get="/maps" hx-target="#middle">store</a>, 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 = <<<HTML
|
||||
Thank you for purchasing this map.<br><br>
|
||||
You may return to <a hx-get="/" hx-target="#middle">town</a>, <a hx-get="/maps" hx-target="#middle">map shop</a>, 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 = <<<HTML
|
||||
You are buying the <b>{$town['name']}</b> map for {$town['mapprice']} gold. Is that ok?<br><br>
|
||||
@ -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 <a href="/" hx-get="/" hx-target="#middle">go back</a> 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 = <<<HTML
|
||||
You have discovered <b>{$town['name']}</b>! It has been added to your mapped towns.<br><br>
|
||||
@ -416,19 +426,19 @@ class Towns
|
||||
$page = 'You are already in this town. <a href="/" hx-get="/" hx-target="#middle">Click here</a> 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 <b>'.$town['name'].'</b>. You may now <a href="/" hx-get="/" hx-target="#middle">enter this town</a>.';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
18
src/DragonKnight/DragonKnight.php
Normal file
18
src/DragonKnight/DragonKnight.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the Dragon-Knight project.
|
||||
*
|
||||
* Copyright (c) 2024-present Sharkk
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace DragonKnight;
|
||||
|
||||
define('VERSION', '1.2.5');
|
||||
define('BUILD', 'Reawaken');
|
||||
define('START', microtime(true));
|
@ -1,731 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the Dragon-Knight project.
|
||||
*
|
||||
* Copyright (c) 2024-present Sharkk
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace DragonKnight;
|
||||
|
||||
use DragonKnight\Actions\Explore;
|
||||
use DragonKnight\Actions\Towns;
|
||||
use DragonKnight\Models\User;
|
||||
|
||||
define('VERSION', '1.2.5');
|
||||
define('BUILD', 'Reawaken');
|
||||
define('START', microtime(true));
|
||||
|
||||
class Lib
|
||||
{
|
||||
/**
|
||||
* Return a page for a couple generic actions.
|
||||
*/
|
||||
public static function index(): string
|
||||
{
|
||||
if (self::user()->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(
|
||||
'<div style="position: absolute; width: 5px; height: 5px; border-radius: 1000px; border: solid 1px black; background-color: red; left: %dpx; top: %dpx;"></div>',
|
||||
round(258 + Lib::user()->longitude * (500 / 500) - 3),
|
||||
round(258 - Lib::user()->latitude * (500 / 500) - 3)
|
||||
);
|
||||
|
||||
echo Lib::render('layouts/minimal', [
|
||||
'content' => '<img src="/img/map.gif" alt="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'].'<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$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 .= '<div class="message">[<b>'.$row['author'].'</b>] '.Lib::make_safe($row['babble']).'</div>';
|
||||
}
|
||||
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 = '<ul>';
|
||||
foreach ($errors as $field => $errors) {
|
||||
$string .= '<li>';
|
||||
foreach ($errors as $error) {
|
||||
$string .= $error;
|
||||
}
|
||||
$string .= '</li>';
|
||||
}
|
||||
|
||||
return $string.'</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
<div class="stat-bar" style="width: 15px; height: 100px; border: solid 1px black;">
|
||||
<div style="height: $percent%; background-image: url(/img/bars_$color.gif);"></div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
public static function create_stat_table(): string
|
||||
{
|
||||
$stat_table = '<div class="stat-table">'.
|
||||
'<div class="stat-row">'.
|
||||
'<div class="stat-col">'.self::generate_stat_bar((int) self::user()->currenthp, (int) self::user()->maxhp).'<div>HP</div></div>'.
|
||||
'<div class="stat-col">'.self::generate_stat_bar((int) self::user()->currentmp, (int) self::user()->maxmp).'<div>MP</div></div>'.
|
||||
'<div class="stat-col">'.self::generate_stat_bar((int) self::user()->currenttp, (int) self::user()->maxtp).'<div>TP</div></div>'.
|
||||
'</div>'.
|
||||
'</div>';
|
||||
|
||||
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 .= '<title>'.self::page_title().'</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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 '<div id="debug-db-info" hx-swap-oob="true">'.$total_time.' Seconds, '.Lib::db()->count.' Queries'.$htmx.'</div>';
|
||||
return '<div id="debug-db-info" hx-swap-oob="true">'.$total_time.' Seconds, '.db()->count.' Queries'.$htmx.'</div>';
|
||||
}
|
||||
|
||||
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)) ? '<blink><span class="highlight"><b>*'.Lib::user()->currenthp.'*</b></span></blink>' : Lib::user()->currenthp;
|
||||
$mp = (Lib::user()->currentmp <= (Lib::user()->maxmp / 5)) ? '<blink><span class="highlight"><b>*'.Lib::user()->currentmp.'*</b></span></blink>' : Lib::user()->currentmp;
|
||||
$hp = (user()->currenthp <= (user()->maxhp / 5)) ? '<blink><span class="highlight"><b>*'.user()->currenthp.'*</b></span></blink>' : user()->currenthp;
|
||||
$mp = (user()->currentmp <= (user()->maxmp / 5)) ? '<blink><span class="highlight"><b>*'.user()->currentmp.'*</b></span></blink>' : user()->currentmp;
|
||||
|
||||
$template = Lib::render('right_nav', ['hp' => $hp, 'mp' => $mp]);
|
||||
if (Lib::is_htmx()) {
|
||||
$template = render('right_nav', ['hp' => $hp, 'mp' => $mp]);
|
||||
if (is_htmx()) {
|
||||
$template = '<section id="right" hx-swap-oob="true">'.$template.'</section>';
|
||||
}
|
||||
|
||||
@ -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 = '<section id="left" hx-swap-oob="true">'.$template.'</section>';
|
||||
}
|
||||
|
||||
@ -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 = '<pre id="debug-query-log" hx-swap-oob="true">';
|
||||
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 .= '<div>['.round($record[1], 2)."s] {$query_string}{$error_string}</div>";
|
||||
|
724
src/DragonKnight/functions.php
Normal file
724
src/DragonKnight/functions.php
Normal file
@ -0,0 +1,724 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the Dragon-Knight project.
|
||||
*
|
||||
* Copyright (c) 2024-present Sharkk
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace DragonKnight;
|
||||
|
||||
use DragonKnight\Actions\Explore;
|
||||
use DragonKnight\Actions\Towns;
|
||||
use DragonKnight\Models\User;
|
||||
|
||||
function index(): string|false
|
||||
{
|
||||
$page = false;
|
||||
|
||||
if (user()->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(
|
||||
'<div style="position: absolute; width: 5px; height: 5px; border-radius: 1000px; border: solid 1px black; background-color: red; left: %dpx; top: %dpx;"></div>',
|
||||
round(258 + user()->longitude * (500 / 500) - 3),
|
||||
round(258 - user()->latitude * (500 / 500) - 3)
|
||||
);
|
||||
|
||||
echo render('layouts/minimal', [
|
||||
'content' => '<img src="/img/map.gif" alt="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'].'<br>';
|
||||
}
|
||||
}
|
||||
|
||||
$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 .= '<div class="message">[<b>'.$row['author'].'</b>] '.make_safe($row['babble']).'</div>';
|
||||
}
|
||||
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 = '<ul>';
|
||||
foreach ($errors as $field => $errors) {
|
||||
$string .= '<li>';
|
||||
foreach ($errors as $error) {
|
||||
$string .= $error;
|
||||
}
|
||||
$string .= '</li>';
|
||||
}
|
||||
|
||||
return $string.'</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
<div class="stat-bar" style="width: 15px; height: 100px; border: solid 1px black;">
|
||||
<div style="height: $percent%; background-image: url(/img/bars_$color.gif);"></div>
|
||||
</div>
|
||||
HTML;
|
||||
}
|
||||
|
||||
function create_stat_table(): string
|
||||
{
|
||||
$stat_table = '<div class="stat-table">'.
|
||||
'<div class="stat-row">'.
|
||||
'<div class="stat-col">'.generate_stat_bar((int) user()->currenthp, (int) user()->maxhp).'<div>HP</div></div>'.
|
||||
'<div class="stat-col">'.generate_stat_bar((int) user()->currentmp, (int) user()->maxmp).'<div>MP</div></div>'.
|
||||
'<div class="stat-col">'.generate_stat_bar((int) user()->currenttp, (int) user()->maxtp).'<div>TP</div></div>'.
|
||||
'</div>'.
|
||||
'</div>';
|
||||
|
||||
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 .= '<title>'.page_title().'</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;
|
||||
}
|
@ -15,7 +15,7 @@ Experience values for each level should be the cumulative total amount of experi
|
||||
<tr><td colspan="2"></td></tr>
|
||||
|
||||
<?php foreach ([1, 2, 3] as $n): ?>
|
||||
<?php $class_name = DragonKnight\Lib::env("class_{$n}_name"); ?>
|
||||
<?php $class_name = DragonKnight\env("class_{$n}_name"); ?>
|
||||
<tr><td><?php echo $class_name ?> EXP</td> <td><input type="number" name="<?php echo $n ?>_exp" value="<?php echo $level["{$n}_exp"] ?>"></td></tr>
|
||||
<tr><td><?php echo $class_name ?> HP</td> <td><input type="number" name="<?php echo $n ?>_hp" value="<?php echo $level["{$n}_hp"] ?>"></td></tr>
|
||||
<tr><td><?php echo $class_name ?> MP</td> <td><input type="number" name="<?php echo $n ?>_mp" value="<?php echo $level["{$n}_mp"] ?>"></td></tr>
|
||||
|
@ -23,9 +23,9 @@
|
||||
<tr><td>Longitude</td><td><input type="number" name="longitude" value="<?php echo $user['longitude'] ?>" /></td></tr>
|
||||
<tr><td>Character Class</td><td>
|
||||
<select name="charclass">
|
||||
<option value="1" <?php echo $user['charclass'] == 1 ? 'selected' : '' ?>><?php echo DragonKnight\Lib::env('class_1_name') ?></option>
|
||||
<option value="2" <?php echo $user['charclass'] == 2 ? 'selected' : '' ?>><?php echo DragonKnight\Lib::env('class_2_name') ?></option>
|
||||
<option value="3" <?php echo $user['charclass'] == 3 ? 'selected' : '' ?>><?php echo DragonKnight\Lib::env('class_3_name') ?></option>
|
||||
<option value="1" <?php echo $user['charclass'] == 1 ? 'selected' : '' ?>><?php echo DragonKnight\env('class_1_name') ?></option>
|
||||
<option value="2" <?php echo $user['charclass'] == 2 ? 'selected' : '' ?>><?php echo DragonKnight\env('class_2_name') ?></option>
|
||||
<option value="3" <?php echo $user['charclass'] == 3 ? 'selected' : '' ?>><?php echo DragonKnight\env('class_3_name') ?></option>
|
||||
</select>
|
||||
</td></tr>
|
||||
|
||||
|
@ -8,8 +8,8 @@
|
||||
<td width="20%"><span class="highlight">Game Open:</span></td>
|
||||
<td>
|
||||
<select name="gameopen">
|
||||
<option value="1" <?php echo DragonKnight\Lib::env('game_open') ? 'selected' : '' ?>>Open</option>
|
||||
<option value="0" <?php echo ! DragonKnight\Lib::env('game_open') ? 'selected' : '' ?>>Closed</option>
|
||||
<option value="1" <?php echo DragonKnight\env('game_open') ? 'selected' : '' ?>>Open</option>
|
||||
<option value="0" <?php echo ! DragonKnight\env('game_open') ? 'selected' : '' ?>>Closed</option>
|
||||
</select><br>
|
||||
<span class="small">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.</span>
|
||||
@ -18,14 +18,14 @@
|
||||
<tr>
|
||||
<td width="20%">Game Name:</td>
|
||||
<td>
|
||||
<input type="text" name="gamename" value="<?php echo DragonKnight\Lib::env('game_name') ?>"><br>
|
||||
<input type="text" name="gamename" value="<?php echo DragonKnight\env('game_name') ?>"><br>
|
||||
<span class="small">Change this if you want to change to call your game something different.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Game URL:</td>
|
||||
<td>
|
||||
<input type="text" name="gameurl" value="<?php echo DragonKnight\Lib::env('game_url') ?>"><br>
|
||||
<input type="text" name="gameurl" value="<?php echo DragonKnight\env('game_url') ?>"><br>
|
||||
<span class="small">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.</span>
|
||||
@ -34,7 +34,7 @@
|
||||
<tr>
|
||||
<td width="20%">Admin Email:</td>
|
||||
<td>
|
||||
<input type="text" name="adminemail" value="<?php echo DragonKnight\Lib::env('admin_email') ?>"><br>
|
||||
<input type="text" name="adminemail" value="<?php echo DragonKnight\env('admin_email') ?>"><br>
|
||||
<span class="small">Please specify your email address. This gets used when the game has to send an
|
||||
email to users.</span>
|
||||
</td>
|
||||
@ -42,7 +42,7 @@
|
||||
<tr>
|
||||
<td width="20%">Map Size:</td>
|
||||
<td>
|
||||
<input type="number" name="gamesize" value="<?php echo DragonKnight\Lib::env('game_size') ?>"><br>
|
||||
<input type="number" name="gamesize" value="<?php echo DragonKnight\env('game_size') ?>"><br>
|
||||
<span class="small">
|
||||
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 @@
|
||||
<td width="20%">Email Verification:</td>
|
||||
<td>
|
||||
<select name="verifyemail">
|
||||
<option value="0" <?php echo ! DragonKnight\Lib::env('verify_email') ? 'selected' : '' ?>>Disabled</option>
|
||||
<option value="1" <?php echo DragonKnight\Lib::env('verify_email') ? 'selected' : '' ?>>Enabled</option>
|
||||
<option value="0" <?php echo ! DragonKnight\env('verify_email') ? 'selected' : '' ?>>Disabled</option>
|
||||
<option value="1" <?php echo DragonKnight\env('verify_email') ? 'selected' : '' ?>>Enabled</option>
|
||||
</select><br>
|
||||
<span class="small">Make users verify their email address for added security.</span>
|
||||
</td>
|
||||
@ -65,8 +65,8 @@
|
||||
<td width="20%">Show News:</td>
|
||||
<td>
|
||||
<select name="shownews">
|
||||
<option value="0" <?php echo ! DragonKnight\Lib::env('show_news') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\Lib::env('show_news') ? 'selected' : '' ?>>Yes</option>
|
||||
<option value="0" <?php echo ! DragonKnight\env('show_news') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\env('show_news') ? 'selected' : '' ?>>Yes</option>
|
||||
</select><br>
|
||||
<span class="small">Toggle display of the Latest News box in towns.
|
||||
</td>
|
||||
@ -75,8 +75,8 @@
|
||||
<td width="20%">Show Who's Online:</td>
|
||||
<td>
|
||||
<select name="showonline">
|
||||
<option value="0" <?php echo ! DragonKnight\Lib::env('show_online') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\Lib::env('show_online') ? 'selected' : '' ?>>Yes</option>
|
||||
<option value="0" <?php echo ! DragonKnight\env('show_online') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\env('show_online') ? 'selected' : '' ?>>Yes</option>
|
||||
</select><br>
|
||||
<span class="small">Toggle display of the Who's Online box in towns.</span>
|
||||
</td>
|
||||
@ -85,23 +85,23 @@
|
||||
<td width="20%">Show Babblebox:</td>
|
||||
<td>
|
||||
<select name="showbabble">
|
||||
<option value="0" <?php echo ! DragonKnight\Lib::env('show_babble') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\Lib::env('show_babble') ? 'selected' : '' ?>>Yes</option>
|
||||
<option value="0" <?php echo ! DragonKnight\env('show_babble') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?php echo DragonKnight\env('show_babble') ? 'selected' : '' ?>>Yes</option>
|
||||
</select><br>
|
||||
<span class="small">Toggle display of the Babble Box in towns.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Class 1 Name:</td>
|
||||
<td><input type="text" name="class1name" value="<?php echo DragonKnight\Lib::env('class_1_name') ?>"><br></td>
|
||||
<td><input type="text" name="class1name" value="<?php echo DragonKnight\env('class_1_name') ?>"><br></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Class 2 Name:</td>
|
||||
<td><input type="text" name="class2name" value="<?php echo DragonKnight\Lib::env('class_2_name') ?>"><br></td>
|
||||
<td><input type="text" name="class2name" value="<?php echo DragonKnight\env('class_2_name') ?>"><br></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Class 3 Name:</td>
|
||||
<td><input type="text" name="class3name" value="<?php echo DragonKnight\Lib::env('class_3_name') ?>"><br></td>
|
||||
<td><input type="text" name="class3name" value="<?php echo DragonKnight\env('class_3_name') ?>"><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
@ -3,14 +3,14 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo DragonKnight\Lib::page_title() ?></title>
|
||||
<title><?php echo DragonKnight\page_title() ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
<script src="/js/htmx.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="admin-container">
|
||||
<header>
|
||||
<h1><?php echo DragonKnight\Lib::env('game_name') ?></h1>
|
||||
<h1><?php echo DragonKnight\env('game_name') ?></h1>
|
||||
<h3>Admin</h3>
|
||||
</header>
|
||||
<main>
|
||||
@ -44,7 +44,7 @@
|
||||
<div>Version <?php echo VERSION ?> <?php echo BUILD ?></div>
|
||||
</footer>
|
||||
|
||||
<?php if (DragonKnight\Lib::env('debug', false)) {
|
||||
<?php if (DragonKnight\env('debug', false)) {
|
||||
echo DragonKnight\Render::debug_query_log();
|
||||
} ?>
|
||||
</div>
|
||||
|
@ -3,12 +3,12 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo DragonKnight\Lib::env('game_name', 'Dragon Knight') ?> Help</title>
|
||||
<title><?php echo DragonKnight\env('game_name', 'Dragon Knight') ?> Help</title>
|
||||
<link rel="stylesheet" href="/css/help.css">
|
||||
</head>
|
||||
<body>
|
||||
<a name="top"></a>
|
||||
<h1><?php echo DragonKnight\Lib::env('game_name', 'Dragon Knight') ?> Help</h1>
|
||||
<h1><?php echo DragonKnight\env('game_name', 'Dragon Knight') ?> Help</h1>
|
||||
[ <a href="/help">Back to Help</a> ]<br>
|
||||
[ <a href="/">Return to Game</a> ]
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?php echo DragonKnight\Lib::page_title() ?></title>
|
||||
<title><?php echo DragonKnight\page_title() ?></title>
|
||||
<link rel="stylesheet" href="/css/dk.css">
|
||||
<script src="/js/htmx.js"></script>
|
||||
|
||||
@ -20,12 +20,12 @@
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="skin-<?php echo DragonKnight\Lib::game_skin() ?>">
|
||||
<body class="skin-<?php echo DragonKnight\game_skin() ?>">
|
||||
<div id="game-container">
|
||||
<header>
|
||||
<a href="/"><img id="logo" src="/img/logo.gif" alt="<?php echo DragonKnight\Lib::env('game_name', 'Dragon Knight') ?>" title="<?php echo DragonKnight\Lib::env('game_name', 'Dragon Knight') ?>"></a>
|
||||
<a href="/"><img id="logo" src="/img/logo.gif" alt="<?php echo DragonKnight\env('game_name', 'Dragon Knight') ?>" title="<?php echo DragonKnight\env('game_name', 'Dragon Knight') ?>"></a>
|
||||
<nav>
|
||||
<?php if (DragonKnight\Lib::user() !== false): ?>
|
||||
<?php if (DragonKnight\user() !== false): ?>
|
||||
<a href='/logout'><img src='/img/button_logout.gif' alt='Log Out' title='Log Out'></a>
|
||||
<?php else: ?>
|
||||
<a href='/login'><img src='/img/button_login.gif' alt='Log In' title='Log In'></a>
|
||||
@ -48,7 +48,7 @@
|
||||
<div>Version <?php echo VERSION ?> <?php echo BUILD ?></div>
|
||||
</footer>
|
||||
|
||||
<?php if (DragonKnight\Lib::env('debug', false)) {
|
||||
<?php if (DragonKnight\env('debug', false)) {
|
||||
echo DragonKnight\Render::debug_query_log();
|
||||
} ?>
|
||||
</div>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<section>
|
||||
<div class="title"><img src="/img/button_location.gif" alt="Location" title="Location"></div>
|
||||
Currently: <?php echo DragonKnight\Lib::user()->currentaction ?><br>
|
||||
Currently: <?php echo DragonKnight\user()->currentaction ?><br>
|
||||
<?php
|
||||
$lat = DragonKnight\Lib::user()->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 @@
|
||||
<section>
|
||||
<div class="title"><img src="/img/button_towns.gif" alt="Towns" title="Towns"></div>
|
||||
<?php
|
||||
if (DragonKnight\Lib::user()->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 <b>{$town['name']}</b>.<br><br>";
|
||||
}
|
||||
?>
|
||||
Travel To:<br>
|
||||
<?php
|
||||
$town_list = explode(',', DragonKnight\Lib::user()->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 @@
|
||||
<a href="/" hx-get="/" hx-target="#middle">Home</a><br>
|
||||
<a href="/forum" hx-get="/forum" hx-target="#middle">Forum</a><br>
|
||||
<a href="/settings">Settings</a><br>
|
||||
<?php if (DragonKnight\Lib::user()->authlevel === 1): ?>
|
||||
<?php if (DragonKnight\user()->authlevel === 1): ?>
|
||||
<a href="/admin">Admin</a><br>
|
||||
<?php endif; ?>
|
||||
<a href="/help">Help</a><br>
|
||||
|
@ -9,9 +9,9 @@
|
||||
<td>Character Class:</td>
|
||||
<td>
|
||||
<select name="charclass">
|
||||
<option value="1"><?php echo DragonKnight\Lib::env('class_1_name') ?></option>
|
||||
<option value="2"><?php echo DragonKnight\Lib::env('class_2_name') ?></option>
|
||||
<option value="3"><?php echo DragonKnight\Lib::env('class_3_name') ?></option>
|
||||
<option value="1"><?php echo DragonKnight\env('class_1_name') ?></option>
|
||||
<option value="2"><?php echo DragonKnight\env('class_2_name') ?></option>
|
||||
<option value="3"><?php echo DragonKnight\env('class_3_name') ?></option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -1,30 +1,30 @@
|
||||
<section>
|
||||
<div class="title"><img src="/img/button_character.gif" alt="Character" title="Character"></div>
|
||||
<b><?php echo DragonKnight\Lib::user()->username ?></b><br>
|
||||
Level: <?php echo DragonKnight\Lib::user()->level ?><br>
|
||||
Exp: <?php echo number_format(DragonKnight\Lib::user()->experience) ?><br>
|
||||
Gold: <?php echo number_format(DragonKnight\Lib::user()->gold) ?><br>
|
||||
<b><?php echo DragonKnight\user()->username ?></b><br>
|
||||
Level: <?php echo DragonKnight\user()->level ?><br>
|
||||
Exp: <?php echo number_format(DragonKnight\user()->experience) ?><br>
|
||||
Gold: <?php echo number_format(DragonKnight\user()->gold) ?><br>
|
||||
HP: <?php echo $hp ?><br>
|
||||
MP: <?php echo $mp ?><br>
|
||||
TP: <?php echo DragonKnight\Lib::user()->currenttp ?><br><br>
|
||||
<?php echo DragonKnight\Lib::create_stat_table() ?><br>
|
||||
TP: <?php echo DragonKnight\user()->currenttp ?><br><br>
|
||||
<?php echo DragonKnight\create_stat_table() ?><br>
|
||||
<a href="javascript:opencharpopup()">Extended Stats</a>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="title"><img src="/img/button_inventory.gif" alt="Inventory" title="Inventory"></div>
|
||||
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> <?php echo DragonKnight\Lib::user()->weaponname ?><br>
|
||||
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> <?php echo DragonKnight\Lib::user()->armorname ?><br>
|
||||
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> <?php echo DragonKnight\Lib::user()->shieldname ?><br>
|
||||
Slot 1: <?php echo DragonKnight\Lib::user()->slot1name ?><br>
|
||||
Slot 2: <?php echo DragonKnight\Lib::user()->slot2name ?><br>
|
||||
Slot 3: <?php echo DragonKnight\Lib::user()->slot3name ?>
|
||||
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> <?php echo DragonKnight\user()->weaponname ?><br>
|
||||
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> <?php echo DragonKnight\user()->armorname ?><br>
|
||||
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> <?php echo DragonKnight\user()->shieldname ?><br>
|
||||
Slot 1: <?php echo DragonKnight\user()->slot1name ?><br>
|
||||
Slot 2: <?php echo DragonKnight\user()->slot2name ?><br>
|
||||
Slot 3: <?php echo DragonKnight\user()->slot3name ?>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="title"><img src="/img/button_fastspells.gif" alt="Fast Spells" title="Fast Spells"></div>
|
||||
<?php
|
||||
$user_spells = DragonKnight\Lib::user()->spells();
|
||||
$user_spells = DragonKnight\user()->spells();
|
||||
if ($user_spells !== false) {
|
||||
foreach ($user_spells as $spell) {
|
||||
// list only healing spells for now
|
||||
|
@ -2,9 +2,9 @@
|
||||
<b><?php echo $char->username ?></b><br><br>
|
||||
|
||||
Class: <?php echo match ($char->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')
|
||||
}; ?><br><br>
|
||||
|
||||
Level: <?php echo $char->level ?><br>
|
||||
|
Loading…
x
Reference in New Issue
Block a user