Start rewriting README, move explore, fix buying maps, fix showchar

This commit is contained in:
Sky Johnson 2024-12-18 23:09:04 -06:00
parent 27f38f7ebc
commit 8b3b1845dc
7 changed files with 141 additions and 163 deletions

View File

@ -1,51 +1,16 @@
# Dragon Knight
- See also: [Dragon Scourge](https://github.com/renderse7en/dragon-scourge)
- [Live Demo](http://dragon.se7enet.com/)
Many years ago, when I was young and dumb, I wrote a simple little game based on the game *Dragon Warrior* for the NES. It was fun, it helped me learn how to code, and a lot of people liked it.
I am now turning it over to the open source community. Fork it, do what you want, make it your own.
Couple things to keep in mind though:
- It's super old. It may not even work on modern versions of PHP. It may have security issues. I have no idea.
- I have moved on with my life, and am no longer changing or doing anything with this game.
- I am not providing help or support. You're on your own.
- I am not accepting pull requests. If you fork this, you are welcome to do whatever you want, but no changes will be merged back into this.
- Quite frankly, I don't really suggest that you use this as is. It's probably better as an inspiration for your own project.
- Have fun with this. I gave it a lot of love a long time ago. I hope it inspires you to give something a lot of love as well.
- This Git repo represents the final released version, 1.1.11, originally released 3/26/2006.
@todo
# System Requirements
- PHP (4.1 and higher)
- MySQL
- zlib compression enabled on your server (optional)
- PHP 8.3+
- PHP SQLite3 Extension
# Installation Instructions
1. Clone this repo or download the zip.
2. Create a new database for Dragon Knight to use, if you don't already have one set up.
3. Edit `config.php` to include the correct values for your database setup.
3. Duplicate `.env.example` as `.env` and change the settings you want.
4. Upload the contents of the Dragon Knight folder to your site.
5. In your browser, run `install.php` and follow the instructions.
6. After completing installation, delete `install.php` from your Dragon Knight directory for security.
5. In your browser, go to `/install` and follow the instructions.
7. Enjoy the game.
# License
MIT License
Copyright (c) 2017 renderse7en
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
See the [license](LICENSE).

View File

@ -16,7 +16,7 @@ $r->get('/', function() {
if (user()->currentaction === "In Town") {
$page = Towns\town();
} elseif (user()->currentaction === "Exploring") {
$page = explore();
$page = Explore\explore();
} elseif (user()->currentaction === "Fighting") {
redirect('/fight');
}
@ -24,6 +24,9 @@ $r->get('/', function() {
return is_htmx() ? $page : display($page, '');
});
/*
NINJA! 🥷
*/
$r->get('/ninja', function() {
exit('NINJA! 🥷');
});
@ -36,7 +39,7 @@ Forum\register_routes($r);
Install\register_routes($r);
Admin\register_routes($r);
$r->post('/move', 'move');
$r->post('/move', 'Explore\move');
$r->get('/spell/:id', 'healspells');
$r->get('/character', 'show_character_info');
@ -64,48 +67,32 @@ if (is_htmx() && $uri[0] !== 'babblebox') {
echo $content;
exit;
/**
* Just spit out a blank exploring page. Exploring without a GET string is normally when they first log in, or when
* they've just finished fighting.
*/
function explore()
{
page_title('Exploring');
return <<<HTML
<div class="title"><img src="/img/title_exploring.gif" alt="Exploring"></div>
You are exploring the map, and nothing has happened. Continue exploring using the direction buttons or the Travel To menus.
HTML;
}
/**
* Show a character's info. Defaults to the currently logged in user.
*/
function show_character_info(int $id = 0): void
function show_character_info(int $id = 0): string
{
global $controlrow, $userrow;
global $controlrow;
$userrow = ($id === 0) ? $userrow : get_user($id);
if ($userrow === false) exit('Failed to show info for user ID '.$id);
$user = $id !== 0 ? User::find($id) : user();
if ($user === false) exit('Failed to show info for user ID '.$id);
$levelrow = db()->query("SELECT `{$userrow["charclass"]}_exp` FROM levels WHERE id=? LIMIT 1;", [$userrow['level'] + 1])->fetchArray(SQLITE3_ASSOC);
$level = db()->query("SELECT `{$user->charclass}_exp` FROM levels WHERE id=? LIMIT 1;", [$user->level + 1])->fetchArray(SQLITE3_ASSOC);
$spells = db()->query('SELECT id, name FROM spells;');
$userspells = explode(',', $userrow['spells']);
$magic_list = '';
while ($spellrow = $spells->fetchArray(SQLITE3_ASSOC)) {
$spell = false;
foreach($userspells as $b) if ($b == $spellrow["id"]) $spell = true;
if ($spell == true) $magic_list .= $spellrow["name"]."<br>";
}
if ($magic_list == "") $magic_list = "None";
$spells = $user->spells();
$magic_list = 'None';
if (!empty($spells)) {
$magic_list = '';
foreach ($spells as $spell) $magic_list .= $spell['name'].'<br>';
}
$showchar = render('showchar', [
'char' => $userrow,
'level' => $levelrow,
'char' => $user,
'level' => $level,
'magic_list' => $magic_list,
'controlrow' => $controlrow
]);
echo render('layouts/minimal', ['content' => $showchar, 'title' => $userrow['username'].' Information']);
return render('layouts/minimal', ['content' => $showchar, 'title' => $user->username.' Information']);
}
function showmap()
@ -127,7 +114,7 @@ function showmap()
*/
function babblebox()
{
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (is_post()) {
$content = trim($_POST["babble"]);
if (!empty($content)) {
db()->query('INSERT INTO babble (posttime, author, babble) VALUES (CURRENT_TIMESTAMP, ?, ?);',
@ -138,7 +125,7 @@ function babblebox()
}
/**
* Is the handler for the HTMX get request for messages.
* The handler that is polled by HTMX for new babblebox messages.
*/
function babblebox_messages(): string
{
@ -154,11 +141,3 @@ function babblebox_messages(): string
if (!$has_chats) $messages = 'There are no messages. :(';
return $messages;
}
/**
* NINJA! 🥷
*/
function ninja(): void
{
exit('NINJA! 🥷');
}

View File

@ -2,6 +2,21 @@
// explore.php :: Handles all map exploring, chances to fight, etc.
namespace Explore;
/**
* Just spit out a blank exploring page. Exploring without a GET string is normally when they first log in, or when
* they've just finished fighting.
*/
function explore()
{
page_title('Exploring');
return <<<HTML
<div class="title"><img src="/img/title_exploring.gif" alt="Exploring"></div>
You are exploring the map, and nothing has happened. Continue exploring using the direction buttons or the Travel To menus.
HTML;
}
function move() {
global $controlrow;
@ -37,7 +52,7 @@ function move() {
// Check for town
$town = get_town_by_xy($longitude, $latitude);
if ($town !== false) {
return Towns\travelto($town['id'], false);
return \Towns\travelto($town['id'], false);
}
// Determine action (1 in 5 chance of fighting)

View File

@ -13,8 +13,7 @@ function register_routes(Router $r): Router
$r->form('/buy/:id', 'Towns\buy');
// $r->get('/sell', 'Towns\sell');
$r->get('/maps', 'Towns\maps');
$r->get('/maps2/:id', 'Towns\maps2');
$r->post('/maps3/:id', 'Towns\maps3');
$r->form('/maps/:id', 'Towns\buy_map');
$r->get('/gotown/:id', 'Towns\travelto');
return $r;
}
@ -88,14 +87,14 @@ function inn()
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 ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['rest']) {
} 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 ($_SERVER['REQUEST_METHOD'] === 'POST' && !$_POST['rest']) {
} elseif (is_post() && !$_POST['rest']) {
redirect('/');
} else {
$page = <<<HTML
@ -182,9 +181,9 @@ function buy(int $id)
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 ($_SERVER['REQUEST_METHOD'] === 'POST' && !$_POST['buy']) {
} elseif (is_post() && !$_POST['buy']) {
redirect('/shop');
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['buy']) {
} elseif (is_post() && $_POST['buy']) {
$type_mapping = [
1 => ['id' => 'weaponid', 'name' => 'weaponname', 'power' => 'attackpower'],
2 => ['id' => 'armorid', 'name' => 'armorname', 'power' => 'defensepower'],
@ -285,68 +284,80 @@ function buy(int $id)
*/
function maps()
{
$mappedtowns = explode(",", user()->towns);
$page = "Buying maps will put the town in your Travel To box, and it won't cost you as many TP to get there.<br><br>\n";
$page .= "Click a town name to purchase its map.<br><br>\n";
$page .= "<table width=\"90%\">\n";
$page = <<<HTML
Buying maps will put the town in your Travel To box, and it won't cost you as many TP to get there.<br><br>
Click a town name to purchase its map.<br><br>
<table>
HTML;
$mapped = explode(',', user()->towns);
$towns = db()->query('SELECT * FROM towns ORDER BY id;');
while ($townrow = $towns->fetchArray(SQLITE3_ASSOC)) {
$latitude = ($townrow["latitude"] >= 0) ? $townrow["latitude"] . "N," : ($townrow["latitude"] * -1) . "S,";
$longitude = ($townrow["longitude"] >= 0) ? $townrow["longitude"] . "E" : ($townrow["longitude"] * -1) . "W";
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";
$mapped = false;
foreach($mappedtowns as $b) if ($b == $townrow["id"]) $mapped = true;
if ($mapped == false) {
$page .= "<tr><td width=\"25%\"><a href=\"/maps2/{$townrow["id"]}\">".$townrow["name"]."</a></td><td width=\"25%\">Price: ".$townrow["mapprice"]." gold</td><td width=\"50%\" colspan=\"2\">Buy map to reveal details.</td></tr>\n";
} else {
$page .= "<tr><td width=\"25%\"><span class=\"light\">".$townrow["name"]."</span></td><td width=\"25%\"><span class=\"light\">Already mapped.</span></td><td width=\"35%\"><span class=\"light\">Location: $latitude $longitude</span></td><td width=\"15%\"><span class=\"light\">TP: ".$townrow["travelpoints"]."</span></td></tr>\n";
}
if (in_array($town['id'], $mapped)) {
$page .= <<<HTML
<tr>
<td width="25%"><span class="light">{$town['name']}</span></td>
<td width="25%"><span class="light">Already mapped.</span></td>
<td width="35%"><span class="light">Location: $latitude $longitude</span></td>
<td width="15%"><span class="light">TP: {$town['travelpoints']}</span></td>
</tr>
HTML;
} else {
$page .= <<<HTML
<tr>
<td width="25%"><a href="/maps/{$town['id']}">{$town['name']}</a></td>
<td width="25%">Price: {$town['mapprice']} gold</td>
<td width="50%" colspan="2">Buy map to reveal details.</td>
</tr>
HTML;
}
}
$page .= "</table><br>\n";
$page .= "If you've changed your mind, you may also return back to <a href=\"/\">town</a>.\n";
$page .= <<<HTML
</table><br>
If you've changed your mind, you may also return back to <a hx-get="/" hx-target="#middle">town</a>.
HTML;
display($page, "Buy Maps");
page_title('Maps');
return is_htmx() ? $page : display($page, '');
}
/**
* Confirm user's intent to purchase map.
*/
function maps2($id)
function buy_map(int $id): string
{
$townrow = get_town_by_id($id);
$town = get_town_by_id($id);
if ($town === false) redirect('/maps');
if (user()->gold < $townrow["mapprice"]) {
display("You do not have enough gold to buy this map.<br><br>You may return to <a href=\"/\">town</a>, <a href=\"/maps\">store</a>, or use the direction buttons on the left to start exploring.", "Buy Maps");
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 (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">store</a>, or use the direction buttons on the left to start exploring.
HTML;
} 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>
<form action="/maps/$id" method="post">
<button name="buy" value="1">Yes</button>
<button name="buy" value="0">No</button>
</form>
HTML;
}
$page = "You are buying the ".$townrow["name"]." map. Is that ok?<br><br><form action=\"/maps3/$id\" method=\"post\"><input type=\"submit\" name=\"submit\" value=\"Yes\" /> <input type=\"submit\" name=\"cancel\" value=\"No\" /></form>";
display($page, "Buy Maps");
}
/**
* Add new map to user's profile.
*/
function maps3($id)
{
if (isset($_POST["cancel"])) redirect('/');
$townrow = get_town_by_id($id);
if (user()->gold < $townrow["mapprice"]) {
display("You do not have enough gold to buy this map.<br><br>You may return to <a href=\"/\">town</a>, <a href=\"/maps\">store</a>, or use the direction buttons on the left to start exploring.", "Buy Maps");
}
$mappedtowns = user()->towns.",$id";
$newgold = user()->gold - $townrow["mapprice"];
db()->query('UPDATE users SET towns=?, gold=? WHERE id=?;', [$mappedtowns, $newgold, user()->id]);
display("Thank you for purchasing this map.<br><br>You may return to <a href=\"/\">town</a>, <a href=\"/maps\">store</a>, or use the direction buttons on the left to start exploring.", "Buy Maps");
page_title('Buying '.$town['name'].' Map');
return is_htmx() ? $page : display($page, '');
}
/**

View File

@ -23,7 +23,7 @@ function login()
{
global $auth;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (is_post()) {
$form = validate($_POST, [
'username' => ['length:3-18', 'alpha-spaces'],
'password' => ['length:6-255'],
@ -187,7 +187,7 @@ function changepassword()
function settings()
{
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (is_post()) {
$form = validate($_POST, [
'game_skin' => ['in:0,1']
]);

View File

@ -558,6 +558,14 @@ function is_htmx(): bool
return isset($_SERVER['HTTP_HX_REQUEST']) && $_SERVER['HTTP_HX_REQUEST'] === 'true';
}
/**
* Return whether the request is POST.
*/
function is_post(): bool
{
return is_post();
}
/**
* Get the current page title per updates. Optionally set a new title.
*/

View File

@ -1,35 +1,35 @@
<div class="title"><img src="/img/button_character.gif" alt="Character" title="Character"></div>
<b><?= $char['username'] ?></b><br><br>
<b><?= $char->username ?></b><br><br>
Class: <?= match ($char['charclass']) {
Class: <?= match ($char->charclass) {
1 => $controlrow["class1name"],
2 => $controlrow["class2name"],
3 => $controlrow["class3name"]
}; ?><br><br>
Level: <?= $char['level'] ?><br>
Experience: <?= number_format($char['experience']) ?>
<?php if ($char['expbonus'] !== 0): ?> <span class="light">(<?= $char['expbonus'] > 0 ? '+' : '' ?>) <?= $char['expbonus'] ?>%)</span> <?php endif; ?><br>
Next Level: <?php if ($char['level'] < 99) { echo number_format($level[$char['charclass']."_exp"]); } else { ?> <span class="light">None</span> <?php } ?><br>
Gold: <?= number_format($char['gold']) ?>
<?php if ($char['goldbonus'] !== 0): ?> <span class="light">(<?= $char['goldbonus'] > 0 ? '+' : '' ?>) <?= $char['goldbonus'] ?>%)</span> <?php endif; ?><br>
Hit Points: <?= number_format($char['currenthp']) ?> / <?= number_format($char['maxhp']) ?><br>
Magic Points: <?= number_format($char['currentmp']) ?> / <?= number_format($char['maxmp']) ?><br>
Travel Points: <?= number_format($char['currenttp']) ?> / <?= number_format($char['maxtp']) ?><br><br>
Level: <?= $char->level ?><br>
Experience: <?= number_format($char->experience) ?>
<?php if ($char->expbonus !== 0): ?> <span class="light">(<?= $char->expbonus > 0 ? '+' : '' ?>) <?= $char->expbonus ?>%)</span> <?php endif; ?><br>
Next Level: <?php if ($char->level < 99) { echo number_format($level[$char->charclass."_exp"]); } else { ?> <span class="light">None</span> <?php } ?><br>
Gold: <?= number_format($char->gold) ?>
<?php if ($char->goldbonus !== 0): ?> <span class="light">(<?= $char->goldbonus > 0 ? '+' : '' ?>) <?= $char->goldbonus ?>%)</span> <?php endif; ?><br>
Hit Points: <?= number_format($char->currenthp) ?> / <?= number_format($char->maxhp) ?><br>
Magic Points: <?= number_format($char->currentmp) ?> / <?= number_format($char->maxmp) ?><br>
Travel Points: <?= number_format($char->currenttp) ?> / <?= number_format($char->maxtp) ?><br><br>
Strength: <?= number_format($char['strength']) ?><br>
Dexterity: <?= number_format($char['dexterity']) ?><br>
Attack Power: <?= number_format($char['attackpower']) ?><br>
Defense Power: <?= number_format($char['defensepower']) ?><br>
Strength: <?= number_format($char->strength) ?><br>
Dexterity: <?= number_format($char->dexterity) ?><br>
Attack Power: <?= number_format($char->attackpower) ?><br>
Defense Power: <?= number_format($char->defensepower) ?><br>
<br>
<div class="title"><img src="/img/button_inventory.gif" alt="Inventory" title="Inventory"></div>
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> Weapon: <?= $char['weaponname'] ?><br>
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> Armor: <?= $char['armorname'] ?><br>
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> Shield: <?= $char['shieldname'] ?><br>
Slot 1: <?= $char['slot1name'] ?><br>
Slot 2: <?= $char['slot2name'] ?><br>
Slot 3: <?= $char['slot3name'] ?>
<img src="/img/icon_weapon.gif" alt="Weapon" title="Weapon"> <?= $char->weaponname ?><br>
<img src="/img/icon_armor.gif" alt="Armor" title="Armor"> <?= $char->armorname ?><br>
<img src="/img/icon_shield.gif" alt="Shield" title="Shield"> <?= $char->shieldname ?><br>
Slot 1: <?= $char->slot1name ?><br>
Slot 2: <?= $char->slot2name ?><br>
Slot 3: <?= $char->slot3name ?>
<br>
<div class="title"><img src="/img/button_spells.gif" alt="Spells" title="Spells"></div>