Part one of admin panel rework
This commit is contained in:
parent
8c667cbd2a
commit
aca8bcc3fe
|
@ -13,7 +13,7 @@ show_babble = true
|
|||
show_online = true
|
||||
|
||||
# Environment
|
||||
debug = true
|
||||
debug = false
|
||||
|
||||
# Email
|
||||
smtp_host = smtp.foobar.com
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
:root {
|
||||
--font-size: 12px;
|
||||
--font-size: 16px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
|
@ -7,12 +13,26 @@ html {
|
|||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 2rem;
|
||||
color: rgb(108, 108, 108);
|
||||
background-color: rgb(245, 245, 245);
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
color: rgb(30, 30, 30);
|
||||
}
|
||||
|
||||
div#admin-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
|
@ -32,13 +52,16 @@ table {
|
|||
}
|
||||
|
||||
a {
|
||||
color: #663300;
|
||||
color: #015df7;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
a:hover {
|
||||
color: #330000;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: hsl(218, 99%, 29%);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.small {
|
||||
font: 10px verdana;
|
||||
}
|
||||
|
@ -59,8 +82,7 @@ a:hover {
|
|||
footer {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
border: solid 1px black;
|
||||
background-color: #eeeeee;
|
||||
font-size: 0.8rem;
|
||||
padding: 0.5rem;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ $r->post('/move', 'Explore\move');
|
|||
$r->get('/spell/:id', 'healspells');
|
||||
$r->get('/character', 'show_character_info');
|
||||
$r->get('/character/:id', 'show_character_info');
|
||||
$r->get('/showmap', 'showmap');
|
||||
$r->get('/showmap', 'show_map');
|
||||
$r->form('/babblebox', 'babblebox');
|
||||
$r->get('/babblebox/messages', 'babblebox_messages');
|
||||
|
||||
|
@ -40,19 +40,7 @@ $r->get('/ninja', function() {
|
|||
$l = $r->lookup($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);
|
||||
|
||||
if (is_int($l)) exit("Error: $l");
|
||||
$content = $l['handler'](...$l['params'] ?? []);
|
||||
if (is_htmx() && $uri[0] !== 'babblebox') {
|
||||
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();
|
||||
}
|
||||
}
|
||||
echo $content;
|
||||
echo render_response($uri, $l['handler'](...$l['params'] ?? []));
|
||||
exit;
|
||||
|
||||
/**
|
||||
|
@ -96,7 +84,10 @@ function show_character_info(int $id = 0): string
|
|||
return render('layouts/minimal', ['content' => $showchar, 'title' => $user->username.' Information']);
|
||||
}
|
||||
|
||||
function showmap()
|
||||
/**
|
||||
* Show the user their position on the current world map. Only works with a game size of 250 and the default towns 😅
|
||||
*/
|
||||
function show_map()
|
||||
{
|
||||
$pos = sprintf(
|
||||
'<div style="position: absolute; width: 5px; height: 5px; border-radius: 1000px; border: solid 1px black; background-color: red; left: %dpx; top: %dpx;"></div>',
|
||||
|
@ -111,7 +102,7 @@ function showmap()
|
|||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
* Handle a POST request to send a new babblebox message.
|
||||
*/
|
||||
function babblebox()
|
||||
{
|
||||
|
|
|
@ -8,16 +8,16 @@ use Router;
|
|||
|
||||
function register_routes(Router $r): Router
|
||||
{
|
||||
if (user('authlevel') === 1) {
|
||||
if (user()->authlevel === 1) {
|
||||
$r->get('/admin', 'Admin\donothing');
|
||||
|
||||
$r->form('/admin/main', 'Admin\primary');
|
||||
|
||||
$r->get('/admin/items', 'Admin\items');
|
||||
$r->form('/admin/items/:id', 'Admin\edititem');
|
||||
$r->form('/admin/items/:id', 'Admin\edit_item');
|
||||
|
||||
$r->get('/admin/drops', 'Admin\drops');
|
||||
$r->form('/admin/drops/:id', 'Admin\editdrop');
|
||||
$r->form('/admin/drops/:id', 'Admin\edit_drop');
|
||||
|
||||
$r->get('/admin/towns', 'Admin\towns');
|
||||
$r->form('/admin/towns/:id', 'Admin\edittown');
|
||||
|
@ -39,18 +39,19 @@ function register_routes(Router $r): Router
|
|||
return $r;
|
||||
}
|
||||
|
||||
function donothing()
|
||||
/**
|
||||
* Home page for the admin panel.
|
||||
*/
|
||||
function donothing(): string
|
||||
{
|
||||
$page = <<<HTML
|
||||
Welcome to the Dragon Knight Administration section. Use the links on the left bar to control and edit various
|
||||
Welcome to the administration section. Use the links on the left bar to control and edit various
|
||||
elements of the game.
|
||||
<br><br>
|
||||
Please note that the control panel has been created mostly as a shortcut for certain individual settings. It is
|
||||
meant for use primarily with editing one thing at a time. If you need to completely replace an entire table
|
||||
(say, to replace all stock monsters with your own new ones), it is suggested that you use a more in-depth
|
||||
database tool such as <a href="http://www.phpmyadmin.net" target="_new">phpMyAdmin</a>. Also, you may want
|
||||
to have a copy of the Dragon Knight development kit, available from the
|
||||
<a href="http://dragon.se7enet.com/dev.php">Dragon Knight homepage</a>.
|
||||
database tool such as <a href="https://sqlitebrowser.org/" target="_new">SQLite Browser</a>.
|
||||
<br><br>
|
||||
Also, you should be aware that certain portions of the DK code are dependent on the formatting of certain
|
||||
database results (for example, the special attributes on item drops). While I have attempted to point these out
|
||||
|
@ -58,18 +59,22 @@ function donothing()
|
|||
because mistakes in the database content may result in script errors or your game breaking completely.
|
||||
HTML;
|
||||
|
||||
display_admin($page, "Admin Home");
|
||||
page_title('Admin');
|
||||
return \Render\content($page, 'layouts/admin');
|
||||
}
|
||||
|
||||
function primary()
|
||||
/**
|
||||
* Main settings that get written to .env
|
||||
*/
|
||||
function primary(): string
|
||||
{
|
||||
if (isset($_POST["submit"])) {
|
||||
if (is_post()) {
|
||||
$form = validate($_POST, [
|
||||
'gamename' => ['alphanum-spaces', 'length:1-20'],
|
||||
'gamename' => ['alphanum-spaces'],
|
||||
'gamesize' => ['int', 'min:5'],
|
||||
'class1name' => ['alpha-spaces', 'length:1-18'],
|
||||
'class2name' => ['alpha-spaces', 'length:1-18'],
|
||||
'class3name' => ['alpha-spaces', 'length:1-18'],
|
||||
'class1name' => ['alpha-spaces'],
|
||||
'class2name' => ['alpha-spaces'],
|
||||
'class3name' => ['alpha-spaces'],
|
||||
'gameopen' => ['bool'],
|
||||
'verifyemail' => ['bool'],
|
||||
'shownews' => ['bool'],
|
||||
|
@ -81,203 +86,153 @@ function primary()
|
|||
$form = $form['data'];
|
||||
if (($form['gamesize'] % 5) != 0) exit('Map size must be divisible by five.');
|
||||
|
||||
db()->query('UPDATE control SET gamename=?, gamesize=?, class1name=?, class2name=?, class3name=?, gameopen=?, verifyemail=?, gameurl=?, adminemail=?, shownews=?, showonline=?, showbabble=? WHERE id=1;', [
|
||||
$form['gamename'], $form['gamesize'], $form['class1name'], $form['class2name'], $form['class3name'], $form['gameopen'], $form['verifyemail'], $form['gameurl'], $form['adminemail'], $form['shownews'], $form['showonline'], $form['showbabble']
|
||||
]);
|
||||
// @todo
|
||||
// write changes to .env
|
||||
|
||||
display_admin("Settings updated.", "Main Settings");
|
||||
$page = 'Main settings updated.';
|
||||
} else {
|
||||
$errorlist = ul_from_validate_errors($form['errors']);
|
||||
display_admin("<b>Errors:</b><br><div style=\"color:red;\">$errorlist</div><br>Please go back and try again.", "Main Settings");
|
||||
$error_list = ul_from_validate_errors($form['errors']);
|
||||
$page = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
Please go back and try again.
|
||||
HTML;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$page = render('admin/main_settings');
|
||||
}
|
||||
|
||||
global $controlrow;
|
||||
|
||||
$page = <<<HTML
|
||||
<b><u>Main Settings</u></b><br>
|
||||
These options control several major settings for the overall game engine.<br><br>
|
||||
<form action="/admin/main" method="post">
|
||||
<table width="90%">
|
||||
<tr><td width="20%"><span class="highlight">Game Open:</span></td><td><select name="gameopen"><option value="1" {{open1select}}>Open</option><option value="0" {{open0select}}>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></td></tr>
|
||||
<tr><td width="20%">Game Name:</td><td><input type="text" name="gamename" value="{{gamename}}" /><br><span class="small">Default is "Dragon Knight". 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="{{gameurl}}" /><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></td></tr>
|
||||
<tr><td width="20%">Admin Email:</td><td><input type="text" name="adminemail" value="{{adminemail}}" /><br><span class="small">Please specify your email address. This gets used when the game has to send an email to users.</span></td></tr>
|
||||
<tr><td width="20%">Map Size:</td><td><input type="text" name="gamesize" value="{{gamesize}}" /><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) monster levels total, otherwise there will be parts of the map without any monsters, or some monsters won't ever get used. Ex: with a map size of 250, you should have 50 monster levels total.</span></td></tr>
|
||||
<tr><td width="20%">Email Verification:</td><td><select name="verifyemail"><option value="0" {{selectverify0}}>Disabled</option><option value="1" {{selectverify1}}>Enabled</option></select><br><span class="small">Make users verify their email address for added security.</span></td></tr>
|
||||
<tr><td width="20%">Show News:</td><td><select name="shownews"><option value="0" {{selectnews0}}>No</option><option value="1" {{selectnews1}}>Yes</option></select><br><span class="small">Toggle display of the Latest News box in towns.</td></tr>
|
||||
<tr><td width="20%">Show Who's Online:</td><td><select name="showonline"><option value="0" {{selectonline0}}>No</option><option value="1" {{selectonline1}}>Yes</option></select><br><span class="small">Toggle display of the Who's Online box in towns.</span></td></tr>
|
||||
<tr><td width="20%">Show Babblebox:</td><td><select name="showbabble"><option value="0" {{selectbabble0}}>No</option><option value="1" {{selectbabble1}}>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="{{class1name}}" /><br></td></tr>
|
||||
<tr><td width="20%">Class 2 Name:</td><td><input type="text" name="class2name" value="{{class2name}}" /><br></td></tr>
|
||||
<tr><td width="20%">Class 3 Name:</td><td><input type="text" name="class3name" value="{{class3name}}" /><br></td></tr>
|
||||
</table>
|
||||
<input type="submit" name="submit" value="Submit"> <input type="reset" name="reset" value="Reset">
|
||||
</form>
|
||||
HTML;
|
||||
|
||||
if ($controlrow["verifyemail"] == 0) { $controlrow["selectverify0"] = "selected=\"selected\" "; } else { $controlrow["selectverify0"] = ""; }
|
||||
if ($controlrow["verifyemail"] == 1) { $controlrow["selectverify1"] = "selected=\"selected\" "; } else { $controlrow["selectverify1"] = ""; }
|
||||
if ($controlrow["shownews"] == 0) { $controlrow["selectnews0"] = "selected=\"selected\" "; } else { $controlrow["selectnews0"] = ""; }
|
||||
if ($controlrow["shownews"] == 1) { $controlrow["selectnews1"] = "selected=\"selected\" "; } else { $controlrow["selectnews1"] = ""; }
|
||||
if ($controlrow["showonline"] == 0) { $controlrow["selectonline0"] = "selected=\"selected\" "; } else { $controlrow["selectonline0"] = ""; }
|
||||
if ($controlrow["showonline"] == 1) { $controlrow["selectonline1"] = "selected=\"selected\" "; } else { $controlrow["selectonline1"] = ""; }
|
||||
if ($controlrow["showbabble"] == 0) { $controlrow["selectbabble0"] = "selected=\"selected\" "; } else { $controlrow["selectbabble0"] = ""; }
|
||||
if ($controlrow["showbabble"] == 1) { $controlrow["selectbabble1"] = "selected=\"selected\" "; } else { $controlrow["selectbabble1"] = ""; }
|
||||
if ($controlrow["gameopen"] == 1) { $controlrow["open1select"] = "selected=\"selected\" "; } else { $controlrow["open1select"] = ""; }
|
||||
if ($controlrow["gameopen"] == 0) { $controlrow["open0select"] = "selected=\"selected\" "; } else { $controlrow["open0select"] = ""; }
|
||||
|
||||
display_admin(parse($page, $controlrow), "Main Settings");
|
||||
page_title('Admin: Main Settings');
|
||||
return \Render\content($page, 'layouts/admin');
|
||||
}
|
||||
|
||||
function items()
|
||||
/**
|
||||
* Show the full list of items that can be edited.
|
||||
*/
|
||||
function items(): string
|
||||
{
|
||||
$items = db()->query('SELECT id, name FROM items ORDER BY id;');
|
||||
$page = "<b><u>Edit Items</u></b><br>Click an item's name to edit it.<br><br><table width=\"50%\">\n";
|
||||
$page = "<h2>Edit Items</h2>Click an item's name to edit it.<br><br><table>\n";
|
||||
|
||||
$hasItems = false;
|
||||
while ($row = $items->fetchArray(SQLITE3_BOTH)) {
|
||||
$hasItems = true;
|
||||
$page .= "<tr><td width=\"8%\" style=\"background-color: #eeeeee;\">".$row["id"]."</td><td style=\"background-color: #eeeeee;\"><a href=\"/admin/items/".$row["id"]."\">".$row["name"]."</a></td></tr>\n";
|
||||
$page .= <<<HTML
|
||||
<tr>
|
||||
<td width="8%" style="background-color: #eeeeee;">{$row["id"]}</td>
|
||||
<td style="background-color: #eeeeee;"><a href="/admin/items/{$row['id']}" hx-get="/admin/items/{$row['id']}" hx-target="#main">{$row["name"]}</a></td>
|
||||
</tr>
|
||||
HTML;
|
||||
}
|
||||
|
||||
if (!$hasItems) $page .= "<tr><td width=\"8%\" style=\"background-color: #eeeeee;\">No items found.</td></tr>\n";
|
||||
display_admin($page . "</table>", "Edit Items");
|
||||
|
||||
page_title('Admin: Items');
|
||||
return \Render\content($page . '</table>', 'layouts/admin');
|
||||
}
|
||||
|
||||
function edititem($id)
|
||||
/**
|
||||
* Shows the form for editing an item via GET, processes edits via POST
|
||||
*/
|
||||
function edit_item(int $id): string
|
||||
{
|
||||
if (isset($_POST["submit"])) {
|
||||
$errors = [];
|
||||
$n = trim($_POST['name'] ?? '');
|
||||
$bc = (int) trim($_POST['buycost'] ?? 0);
|
||||
$a = (int) trim($_POST['attribute'] ?? 0);
|
||||
$s = trim($_POST['special'] ?? 'X');
|
||||
|
||||
if (empty($n)) $errors[] = "Name is required.";
|
||||
if (!is_int($bc) || !($bc >= 0)) $errors[] = 'Cost must be a number greater than or equal to 0.';
|
||||
if (!is_int($a)) $errors[] = 'Attribute must be a number.';
|
||||
|
||||
if (count($errors) === 0) {
|
||||
db()->query('UPDATE items SET name=?, type=?, buycost=?, attribute=?, special=? WHERE id=?;', [
|
||||
$n, $_POST['type'] ?? 0, $bc, $a, $s, $id
|
||||
]);
|
||||
display_admin("Item updated.","Edit Items");
|
||||
} else {
|
||||
$errorlist = implode('<br>', $errors);
|
||||
display_admin("<b>Errors:</b><br><div style=\"color:red;\">$errorlist</div><br>Please go back and try again.", "Edit Items");
|
||||
}
|
||||
}
|
||||
|
||||
$item = get_item($id);
|
||||
|
||||
$page = <<<HTML
|
||||
<b><u>Edit Items</u></b><br><br>
|
||||
<form action="/admin/items/$id" method="post">
|
||||
<table width="90%">
|
||||
<tr><td width="20%">ID:</td><td>{{id}}</td></tr>
|
||||
<tr><td width="20%">Name:</td><td><input type="text" name="name" value="{{name}}" /></td></tr>
|
||||
<tr><td width="20%">Type:</td><td><select name="type"><option value="1" {{type1select}}>Weapon</option><option value="2" {{type2select}}>Armor</option><option value="3" {{type3select}}>Shield</option></select></td></tr>
|
||||
<tr><td width="20%">Cost:</td><td><input type="text" name="buycost" value="{{buycost}}" /> gold</td></tr>
|
||||
<tr><td width="20%">Attribute:</td><td><input type="text" name="attribute" value="{{attribute}}" /><br><span class="small">How much the item adds to total attackpower (weapons) or defensepower (armor/shields).</span></td></tr>
|
||||
<tr><td width="20%">Special:</td><td><input type="text" name="special" value="{{special}}" /><br><span class="small">Should be either a special code or <span class="highlight">X</span> to disable. Edit this field very carefully because mistakes to formatting or field names can create problems in the game.</span></td></tr>
|
||||
</table>
|
||||
<input type="submit" name="submit" value="Submit" /> <input type="reset" name="reset" value="Reset" />
|
||||
</form>
|
||||
<b>Special Codes:</b><br>
|
||||
Special codes can be added in the item's Special field to give it extra user attributes. Special codes are in the format <span class="highlight">attribute,value</span>. <span class="highlight">Attribute</span> can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. <span class="highlight">Value</span> may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be <span class="highlight">maxhp,50</span>.<br><br>
|
||||
Suggested user fields for special codes:<br>
|
||||
maxhp - max hit points<br>
|
||||
maxmp - max magic points<br>
|
||||
maxtp - max travel points<br>
|
||||
goldbonus - gold bonus, in percent<br>
|
||||
expbonus - experience bonus, in percent<br>
|
||||
strength - strength (which also adds to attackpower)<br>
|
||||
dexterity - dexterity (which also adds to defensepower)<br>
|
||||
attackpower - total attack power<br>
|
||||
defensepower - total defense power
|
||||
HTML;
|
||||
if (is_post()) {
|
||||
$form = validate($_POST, [
|
||||
'name' => [],
|
||||
'type' => ['int', 'in:1,2,3'],
|
||||
'buycost' => ['int', 'min:0'],
|
||||
'attribute' => ['int', 'min:0'],
|
||||
'special' => ['default:X']
|
||||
]);
|
||||
|
||||
if ($item["type"] == 1) { $item["type1select"] = "selected=\"selected\" "; } else { $item["type1select"] = ""; }
|
||||
if ($item["type"] == 2) { $item["type2select"] = "selected=\"selected\" "; } else { $item["type2select"] = ""; }
|
||||
if ($item["type"] == 3) { $item["type3select"] = "selected=\"selected\" "; } else { $item["type3select"] = ""; }
|
||||
if ($form['valid']) {
|
||||
$f = $form['data'];
|
||||
db()->query('UPDATE items SET name=?, type=?, buycost=?, attribute=?, special=? WHERE id=?;', [
|
||||
$f['name'], $f['type'], $f['buycost'], $f['attribute'], $f['special'], $id
|
||||
]);
|
||||
$page = '<b>'.$item['name'].'</b> updated.';
|
||||
} else {
|
||||
$error_list = ul_from_validate_errors($form['errors']);
|
||||
$page = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
Please go back and try again.
|
||||
HTML;
|
||||
}
|
||||
} else {
|
||||
$page = render('admin/edit_item', ['item' => $item]);
|
||||
}
|
||||
|
||||
display_admin(parse($page, $item), "Edit Items");
|
||||
page_title('Admin: Editing '.$item['name']);
|
||||
return \Render\content($page, 'layouts/admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the full list of drops that can be edited
|
||||
*/
|
||||
function drops()
|
||||
{
|
||||
$page = "<b><u>Edit Drops</u></b><br>Click an item's name to edit it.<br><br><table width=\"50%\">\n";
|
||||
$page = "<h2>Edit Drops</h2>Click an item's name to edit it.<br><br><table>\n";
|
||||
|
||||
$drops = db()->query('SELECT id, name FROM drops ORDER BY id;');
|
||||
$has_drops = false;
|
||||
while ($row = $drops->fetchArray(SQLITE3_ASSOC)) {
|
||||
$has_drops = true;
|
||||
$page .= "<tr><td width=\"8%\" style=\"background-color: #eeeeee;\">".$row["id"]."</td><td style=\"background-color: #eeeeee;\"><a href=\"/admin/drops/".$row["id"]."\">".$row["name"]."</a></td></tr>\n";
|
||||
$page .= <<<HTML
|
||||
<tr>
|
||||
<td width="8%" style="background-color: #eeeeee;">{$row["id"]}</td>
|
||||
<td style="background-color: #eeeeee;"><a href="/admin/drops/{$row['id']}" hx-get="/admin/drops/{$row['id']}" hx-target="#main">{$row["name"]}</a></td>
|
||||
</tr>
|
||||
HTML;
|
||||
}
|
||||
|
||||
if (!$has_drops) { $page .= "<tr><td width=\"8%\" style=\"background-color: #eeeeee;\">No drops found.</td></tr>\n"; }
|
||||
|
||||
display_admin($page . "</table>", "Edit Drops");
|
||||
page_title('Admin: Drops');
|
||||
return \Render\content($page . '</table>', 'layouts/admin');
|
||||
}
|
||||
|
||||
function editdrop($id)
|
||||
/**
|
||||
* Show the form to edit drops via GET, process those edits via POST
|
||||
*/
|
||||
function edit_drop(int $id): string
|
||||
{
|
||||
if (isset($_POST["submit"])) {
|
||||
$errors = [];
|
||||
|
||||
$n = trim($_POST['name'] ?? '');
|
||||
$ml = (int) trim($_POST['mlevel'] ?? 0);
|
||||
$a = trim($_POST['attribute1'] ?? 'X');
|
||||
$a2 = trim($_POST['attribute2'] ?? 'X');
|
||||
|
||||
if (empty($n)) $errors[] = "Name is required.";
|
||||
if (!is_int($ml) || $ml < 1) $errors[] = "Monster level is required, and must be higher than 0.";
|
||||
if (empty($a) || $a === 'X') $errors[] = 'First attribute is required.';
|
||||
if (empty($a2)) $a2 = 'X';
|
||||
|
||||
if (count($errors) === 0) {
|
||||
db()->query('UPDATE drops SET name=?, mlevel=?, attribute1=?, attribute2=? WHERE id=?;', [
|
||||
$n, $ml, $a, $a2, $id
|
||||
]);
|
||||
display_admin("Item updated.","Edit Drops");
|
||||
} else {
|
||||
$errorlist = implode('<br>', $errors);
|
||||
display_admin("<b>Errors:</b><br><div style=\"color:red;\">$errorlist</div><br>Please go back and try again.", "Edit Drops");
|
||||
}
|
||||
}
|
||||
|
||||
$drop = get_drop($id);
|
||||
|
||||
$page = <<<HTML
|
||||
<b><u>Edit Drops</u></b><br><br>
|
||||
<form action="/admin/drops/$id" method="post">
|
||||
<table width="90%">
|
||||
<tr><td width="20%">ID:</td><td>{{id}}</td></tr>
|
||||
<tr><td width="20%">Name:</td><td><input type="text" name="name" value="{{name}}" /></td></tr>
|
||||
<tr><td width="20%">Monster Level:</td><td><input type="number" name="mlevel" value="{{mlevel}}" /><br><span class="small">Minimum monster level that will drop this item.</span></td></tr>
|
||||
<tr><td width="20%">Attribute 1:</td><td><input type="text" name="attribute1" value="{{attribute1}}" /><br><span class="small">Must be a special code. First attribute cannot be disabled. Edit this field very carefully because mistakes to formatting or field names can create problems in the game.</span></td></tr>
|
||||
<tr><td width="20%">Attribute 2:</td><td><input type="text" name="attribute2" value="{{attribute2}}" /><br><span class="small">Should be either a special code or <span class="highlight">X</span> to disable. Edit this field very carefully because mistakes to formatting or field names can create problems in the game.</span></td></tr>
|
||||
</table>
|
||||
<input type="submit" name="submit" value="Submit" /> <input type="reset" name="reset" value="Reset" />
|
||||
</form>
|
||||
<b>Special Codes:</b><br>
|
||||
Special codes are used in the two attribute fields to give the item properties. The first attribute field must contain a special code, but the second one may be left empty ("X") if you wish. Special codes are in the format <span class="highlight">attribute,value</span>. <span class="highlight">Attribute</span> can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. <span class="highlight">Value</span> may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be <span class="highlight">maxhp,50</span>.<br><br>
|
||||
Suggested user fields for special codes:<br>
|
||||
maxhp - max hit points<br>
|
||||
maxmp - max magic points<br>
|
||||
maxtp - max travel points<br>
|
||||
goldbonus - gold bonus, in percent<br>
|
||||
expbonus - experience bonus, in percent<br>
|
||||
strength - strength (which also adds to attackpower)<br>
|
||||
dexterity - dexterity (which also adds to defensepower)<br>
|
||||
attackpower - total attack power<br>
|
||||
defensepower - total defense power
|
||||
HTML;
|
||||
if (is_post()) {
|
||||
$form = validate($_POST, [
|
||||
'name' => [],
|
||||
'mlevel' => ['int', 'min:1'],
|
||||
'attribute1' => [],
|
||||
'attribute2' => ['default:X'],
|
||||
]);
|
||||
|
||||
display_admin(parse($page, $drop), "Edit Drops");
|
||||
if ($form['valid']) {
|
||||
db()->query('UPDATE drops SET name=?, mlevel=?, attribute1=?, attribute2=? WHERE id=?;', [
|
||||
$form['data']['name'], $form['data']['mlevel'], $form['data']['attribute1'], $form['data']['attribute2'], $id
|
||||
]);
|
||||
$page = '<b>'.$form['data']['name'].'</b> updated.';
|
||||
} else {
|
||||
$error_list = ul_from_validate_errors($form['errors']);
|
||||
$page = <<<HTML
|
||||
<b>Errors:</b><br>
|
||||
<div style="color: red;">{$error_list}</div><br>
|
||||
Please go back and try again.
|
||||
HTML;
|
||||
}
|
||||
} else {
|
||||
$page = render('admin/edit_drop', ['drop' => $drop]);
|
||||
}
|
||||
|
||||
page_title('Admin: Editing '.$drop['name']);
|
||||
return \Render\content($page, 'layouts/admin');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the list of towns that can be edited.
|
||||
*/
|
||||
function towns()
|
||||
{
|
||||
$page = "<b><u>Edit Towns</u></b><br>Click an town's name to edit it.<br><br><table width=\"50%\">\n";
|
||||
|
|
32
src/lib.php
32
src/lib.php
|
@ -82,11 +82,7 @@ function display_admin($content, $title)
|
|||
{
|
||||
echo render('layouts/admin', [
|
||||
"title" => $title,
|
||||
"content" => $content,
|
||||
"totaltime" => round(microtime(true) - START, 4),
|
||||
"numqueries" => db()->count,
|
||||
"version" => VERSION,
|
||||
"build" => BUILD
|
||||
"content" => $content
|
||||
]);
|
||||
|
||||
exit;
|
||||
|
@ -511,3 +507,29 @@ function page_title(string $new_title = ''): string
|
|||
if ($new_title) return $GLOBALS['state']['new-page-title'] = $new_title;
|
||||
return $GLOBALS['state']['new-page-title'] ?? env('game_name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the response for the browser based on the request context. The main point is to seperate the handling
|
||||
* of HTMX responses from normal responses.
|
||||
*/
|
||||
function render_response(array $uri, string $content): string
|
||||
{
|
||||
if (!is_htmx() || $uri[0] === 'babblebox') return $content;
|
||||
|
||||
header('HX-Push-Url: '.$_SERVER['REQUEST_URI']);
|
||||
|
||||
$content .= '<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 $content;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ class User extends Model
|
|||
*/
|
||||
public function update_online_time(): void
|
||||
{
|
||||
if ($this->onlinetime && strtotime($this->onlinetime) > strtotime('-9 minutes')) return;
|
||||
db()->query('UPDATE users SET onlinetime=CURRENT_TIMESTAMP WHERE id=?;', [$this->id]);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ namespace Render;
|
|||
|
||||
/**
|
||||
* Prepare content for final render. If the request is HTMX-based, will return just the content passed to it. Otherwise
|
||||
* it will render() onto layouts/primary with some additional bits.
|
||||
* it will render() onto $layout with some additional bits.
|
||||
*/
|
||||
function content(string $content): string
|
||||
function content(string $content, string $layout = 'layouts/primary'): string
|
||||
{
|
||||
if (is_htmx()) return $content;
|
||||
return render('layouts/primary', ['content' => $content]);
|
||||
return render($layout, ['content' => $content]);
|
||||
}
|
||||
|
||||
function debug_db_info(): string {
|
||||
|
|
49
templates/admin/edit_drop.php
Normal file
49
templates/admin/edit_drop.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<h2>Editing <?= $drop['name'] ?></h2>
|
||||
<form action="/admin/drops/<?= $drop['id'] ?>" method="post" hx-post="/admin/drops/<?= $drop['id'] ?>" hx-target="#main">
|
||||
<table width="90%">
|
||||
<tr><td width="20%">ID:</td><td><?= $drop['id'] ?></td></tr>
|
||||
<tr><td width="20%">Name:</td><td><input type="text" name="name" value="<?= $drop['name'] ?>"></td></tr>
|
||||
<tr>
|
||||
<td width="20%">Monster Level:</td>
|
||||
<td>
|
||||
<input type="number" name="mlevel" value="<?= $drop['mlevel'] ?>"><br>
|
||||
<span class="small">Minimum monster level that will drop this item.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Attribute 1:</td>
|
||||
<td>
|
||||
<input type="text" name="attribute1" value="<?= $drop['attribute1'] ?>"><br>
|
||||
<span class="small">Must be a special code. First attribute cannot be disabled. Edit this field very
|
||||
carefully because mistakes to formatting or field names can create problems in the game.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Attribute 2:</td>
|
||||
<td>
|
||||
<input type="text" name="attribute2" value="<?= $drop['attribute2'] ?>"><br>
|
||||
<span class="small">Should be either a special code or <span class="highlight">X</span> to
|
||||
disable. Edit this field very carefully because mistakes to formatting or field names can create
|
||||
problems in the game.</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button type="submit">Save</button>
|
||||
<button type="reset">Reset</button>
|
||||
</form>
|
||||
|
||||
<br>
|
||||
|
||||
<h3>Special Codes</h3>
|
||||
Special codes are used in the two attribute fields to give the item properties. The first attribute field must contain a special code, but the second one may be left empty ("X") if you wish. Special codes are in the format <span class="highlight">attribute,value</span>. <span class="highlight">Attribute</span> can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. <span class="highlight">Value</span> may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be <span class="highlight">maxhp,50</span>.<br><br>
|
||||
Suggested user fields for special codes:<br>
|
||||
maxhp - max hit points<br>
|
||||
maxmp - max magic points<br>
|
||||
maxtp - max travel points<br>
|
||||
goldbonus - gold bonus, in percent<br>
|
||||
expbonus - experience bonus, in percent<br>
|
||||
strength - strength (which also adds to attackpower)<br>
|
||||
dexterity - dexterity (which also adds to defensepower)<br>
|
||||
attackpower - total attack power<br>
|
||||
defensepower - total defense power
|
55
templates/admin/edit_item.php
Normal file
55
templates/admin/edit_item.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<h2>Edit <?= $item['name'] ?></h2>
|
||||
<form action="/admin/items/<?= $item['id'] ?>" method="post" hx-post="/admin/items/<?= $item['id'] ?>" hx-target="#main">
|
||||
<table>
|
||||
<tr><td width="20%">ID:</td><td><?= $item['id'] ?></td></tr>
|
||||
<tr>
|
||||
<td width="20%">Name:</td>
|
||||
<td><input type="text" name="name" value="<?= $item['name'] ?>"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Type:</td>
|
||||
<td><select name="type">
|
||||
<option value="1" <?= $item['type'] === 1 ? 'selected' : '' ?>>Weapon</option>
|
||||
<option value="2" <?= $item['type'] === 2 ? 'selected' : '' ?>>Armor</option>
|
||||
<option value="3" <?= $item['type'] === 3 ? 'selected' : '' ?>>Shield</option>
|
||||
</select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Cost:</td>
|
||||
<td><input type="text" name="buycost" value="<?= $item['buycost'] ?>"> gold</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Attribute:</td>
|
||||
<td>
|
||||
<input type="number" name="attribute" value="<?= $item['attribute'] ?>"><br>
|
||||
<span class="small">How much the item adds to total attackpower (weapons) or defensepower (armor/shields).</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Special:</td>
|
||||
<td>
|
||||
<input type="text" name="special" value="<?= $item['special'] ?>"><br>
|
||||
<span class="small">Should be either a special code or <span class="highlight">X</span> to disable. Edit
|
||||
this field very carefully because mistakes to formatting or field names can create problems in the game.</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button type="submit">Save</button>
|
||||
<button type="reset">Reset</button>
|
||||
</form>
|
||||
|
||||
<br>
|
||||
|
||||
<h3>Special Codes</h3>
|
||||
Special codes can be added in the item's Special field to give it extra user attributes. Special codes are in the format <span class="highlight">attribute,value</span>. <span class="highlight">Attribute</span> can be any database field from the Users table - however, it is suggested that you only use the ones from the list below, otherwise things can get freaky. <span class="highlight">Value</span> may be any positive or negative whole number. For example, if you want a weapon to give an additional 50 max hit points, the special code would be <span class="highlight">maxhp,50</span>.<br><br>
|
||||
Suggested user fields for special codes:<br>
|
||||
maxhp - max hit points<br>
|
||||
maxmp - max magic points<br>
|
||||
maxtp - max travel points<br>
|
||||
goldbonus - gold bonus, in percent<br>
|
||||
expbonus - experience bonus, in percent<br>
|
||||
strength - strength (which also adds to attackpower)<br>
|
||||
dexterity - dexterity (which also adds to defensepower)<br>
|
||||
attackpower - total attack power<br>
|
||||
defensepower - total defense power
|
110
templates/admin/main_settings.php
Normal file
110
templates/admin/main_settings.php
Normal file
|
@ -0,0 +1,110 @@
|
|||
<h2>Main Settings</h2>
|
||||
<p>These options control several major settings for the game engine.</p>
|
||||
<p>Note that these particular settings are written to the .env file in the root directory, and not the database.</p>
|
||||
<br>
|
||||
<form action="/admin/main" method="post" hx-post="/admin/main" hx-target="#main">
|
||||
<table>
|
||||
<tr>
|
||||
<td width="20%"><span class="highlight">Game Open:</span></td>
|
||||
<td>
|
||||
<select name="gameopen">
|
||||
<option value="1" <?= env('game_open') ? 'selected' : '' ?>>Open</option>
|
||||
<option value="0" <?= !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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Game Name:</td>
|
||||
<td>
|
||||
<input type="text" name="gamename" value="<?= 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="<?= 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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Admin Email:</td>
|
||||
<td>
|
||||
<input type="text" name="adminemail" value="<?= 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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Map Size:</td>
|
||||
<td>
|
||||
<input type="number" name="gamesize" value="<?= 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)
|
||||
monster levels total, otherwise there will be parts of the map without any monsters, or some
|
||||
monsters won't ever get used. Ex: with a map size of 250, you should have 50 monster levels total.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Email Verification:</td>
|
||||
<td>
|
||||
<select name="verifyemail">
|
||||
<option value="0" <?= !env('verify_email') ? 'selected' : '' ?>>Disabled</option>
|
||||
<option value="1" <?= env('verify_email') ? 'selected' : '' ?>>Enabled</option>
|
||||
</select><br>
|
||||
<span class="small">Make users verify their email address for added security.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Show News:</td>
|
||||
<td>
|
||||
<select name="shownews">
|
||||
<option value="0" <?= !env('show_news') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?= env('show_news') ? 'selected' : '' ?>>Yes</option>
|
||||
</select><br>
|
||||
<span class="small">Toggle display of the Latest News box in towns.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Show Who's Online:</td>
|
||||
<td>
|
||||
<select name="showonline">
|
||||
<option value="0" <?= !env('show_online') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?= env('show_online') ? 'selected' : '' ?>>Yes</option>
|
||||
</select><br>
|
||||
<span class="small">Toggle display of the Who's Online box in towns.</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Show Babblebox:</td>
|
||||
<td>
|
||||
<select name="showbabble">
|
||||
<option value="0" <?= !env('show_babble') ? 'selected' : '' ?>>No</option>
|
||||
<option value="1" <?= 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="<?= env('class_1_name') ?>"><br></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Class 2 Name:</td>
|
||||
<td><input type="text" name="class2name" value="<?= env('class_2_name') ?>"><br></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">Class 3 Name:</td>
|
||||
<td><input type="text" name="class3name" value="<?= env('class_3_name') ?>"><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<button type="submit">Save</button>
|
||||
<button type="reset">Reset</button>
|
||||
</form>
|
|
@ -3,34 +3,36 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $title ?></title>
|
||||
<title><?= page_title() ?></title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
<script src="/js/htmx.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="admin-container">
|
||||
<header>
|
||||
<h1><?= env('game_name') ?></h1>
|
||||
<h3>Admin</h3>
|
||||
</header>
|
||||
<main>
|
||||
<nav>
|
||||
<h1>Admin Panel</h1>
|
||||
|
||||
<b>Links:</b><br>
|
||||
<a href="/admin">Admin Home</a><br>
|
||||
<a href="/admin" hx-get="/admin" hx-target="#main">Admin Home</a><br>
|
||||
<a href="/">Game Home</a><br><br>
|
||||
|
||||
<b>Primary Data:</b><br>
|
||||
<a href="/admin/main">Main Settings</a><br>
|
||||
<a href="/admin/news">Add News Post</a><br>
|
||||
<a href="/admin/users">Edit Users</a><br><br>
|
||||
<br>
|
||||
<a href="/admin/main" hx-get="/admin/main" hx-target="#main">Main Settings</a><br>
|
||||
<a href="/admin/news" hx-get="/admin/news" hx-target="#main">Add News Post</a><br>
|
||||
<a href="/admin/users" hx-get="/admin/users" hx-target="#main">Edit Users</a><br><br>
|
||||
|
||||
<b>Game Data:</b><br>
|
||||
<a href="/admin/items">Edit Items</a><br>
|
||||
<a href="/admin/drops">Edit Drops</a><br>
|
||||
<a href="/admin/towns">Edit Towns</a><br>
|
||||
<a href="/admin/monsters">Edit Monsters</a><br>
|
||||
<a href="/admin/level">Edit Levels</a><br>
|
||||
<a href="/admin/spells">Edit Spells</a><br>
|
||||
<br>
|
||||
<a href="/admin/items" hx-get="/admin/items" hx-target="#main">Edit Items</a><br>
|
||||
<a href="/admin/drops" hx-get="/admin/drops" hx-target="#main">Edit Drops</a><br>
|
||||
<a href="/admin/towns" hx-get="/admin/towns" hx-target="#main">Edit Towns</a><br>
|
||||
<a href="/admin/monsters" hx-get="/admin/monsters" hx-target="#main">Edit Monsters</a><br>
|
||||
<a href="/admin/level" hx-get="/admin/level" hx-target="#main">Edit Levels</a><br>
|
||||
<a href="/admin/spells" hx-get="/admin/spells" hx-target="#main">Edit Spells</a><br>
|
||||
</nav>
|
||||
|
||||
<section>
|
||||
<section id="main">
|
||||
<?= $content ?>
|
||||
</section>
|
||||
</main>
|
||||
|
@ -38,9 +40,11 @@
|
|||
<footer>
|
||||
<div>Powered by <a href="/" target="_new">Dragon Knight</a></div>
|
||||
<div>© 2024 Sharkk</div>
|
||||
<div><?= $totaltime ?> Seconds, <?= $numqueries ?> Queries</div>
|
||||
<div>Version <?= $version ?> <?= $build ?></div>
|
||||
<?= Render\debug_db_info(); ?>
|
||||
<div>Version <?= VERSION ?> <?= BUILD ?></div>
|
||||
</footer>
|
||||
|
||||
<?php if (env('debug', false)) echo Render\debug_query_log(); ?>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue
Block a user