diff --git a/README.md b/README.md index a4e45b8..987bec6 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/public/index.php b/public/index.php index dc7b908..09f6ffc 100644 --- a/public/index.php +++ b/public/index.php @@ -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 <<Exploring - 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"]."
"; - } - 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'].'
'; + } $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! 🥷'); -} diff --git a/src/actions/explore.php b/src/actions/explore.php index bf7efa8..34f9098 100644 --- a/src/actions/explore.php +++ b/src/actions/explore.php @@ -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 <<Exploring + 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) diff --git a/src/actions/towns.php b/src/actions/towns.php index b94c10b..0be19ce 100644 --- a/src/actions/towns.php +++ b/src/actions/towns.php @@ -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.

You may return to town, 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 = <<
You may return to town, 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 = <<town, shop, 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.

\n"; - $page .= "Click a town name to purchase its map.

\n"; - $page .= "\n"; + $page = <<
+ Click a town name to purchase its map.

+
+ 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 .= "\n"; - } else { - $page .= "\n"; - } + if (in_array($town['id'], $mapped)) { + $page .= << + + + + + + HTML; + } else { + $page .= << + + + + + HTML; + } } - $page .= "
".$townrow["name"]."Price: ".$townrow["mapprice"]." goldBuy map to reveal details.
".$townrow["name"]."Already mapped.Location: $latitude $longitudeTP: ".$townrow["travelpoints"]."
{$town['name']}Already mapped.Location: $latitude $longitudeTP: {$town['travelpoints']}
{$town['name']}Price: {$town['mapprice']} goldBuy map to reveal details.

\n"; - $page .= "If you've changed your mind, you may also return back to town.\n"; + $page .= <<
+ If you've changed your mind, you may also return back to town. + 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.

You may return to town, store, or use the direction buttons on the left to start exploring.", "Buy Maps"); + if (user()->gold < $town['mapprice']) { + $page = <<
+ You may return to town, store, or use the direction buttons on the left to start exploring. + HTML; + } elseif (is_post() && $_POST['buy']) { + user()->towns .= ",$id"; + user()->gold -= $town['mapprice']; + user()->save(); + + $page = <<
+ You may return to town, store, or use the direction buttons on the left to start exploring. + HTML; + } elseif (is_post() && !$_POST['buy']) { + redirect('/maps'); + } else { + $page = <<{$town['name']} map for {$town['mapprice']} gold. Is that ok?

+
+ + +
+ HTML; } - $page = "You are buying the ".$townrow["name"]." map. Is that ok?

"; - - 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.

You may return to town, store, 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.

You may return to town, store, 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, ''); } /** diff --git a/src/actions/users.php b/src/actions/users.php index b629765..2e95c82 100644 --- a/src/actions/users.php +++ b/src/actions/users.php @@ -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'] ]); diff --git a/src/lib.php b/src/lib.php index 9700e92..a7e1b13 100644 --- a/src/lib.php +++ b/src/lib.php @@ -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. */ diff --git a/templates/showchar.php b/templates/showchar.php index ad6b372..5bd54e0 100644 --- a/templates/showchar.php +++ b/templates/showchar.php @@ -1,35 +1,35 @@
Character
-

+username ?>

-Class: charclass) { 1 => $controlrow["class1name"], 2 => $controlrow["class2name"], 3 => $controlrow["class3name"] }; ?>

-Level:
-Experience: - ( 0 ? '+' : '' ?>) %)
-Next Level: None
-Gold: - ( 0 ? '+' : '' ?>) %)
-Hit Points: /
-Magic Points: /
-Travel Points: /

+Level: level ?>
+Experience: experience) ?> + expbonus !== 0): ?> (expbonus > 0 ? '+' : '' ?>) expbonus ?>%)
+Next Level: level < 99) { echo number_format($level[$char->charclass."_exp"]); } else { ?> None
+Gold: gold) ?> + goldbonus !== 0): ?> (goldbonus > 0 ? '+' : '' ?>) goldbonus ?>%)
+Hit Points: currenthp) ?> / maxhp) ?>
+Magic Points: currentmp) ?> / maxmp) ?>
+Travel Points: currenttp) ?> / maxtp) ?>

-Strength:
-Dexterity:
-Attack Power:
-Defense Power:
+Strength: strength) ?>
+Dexterity: dexterity) ?>
+Attack Power: attackpower) ?>
+Defense Power: defensepower) ?>

Inventory
-Weapon Weapon:
-Armor Armor:
-Shield Shield:
-Slot 1:
-Slot 2:
-Slot 3: +Weapon weaponname ?>
+Armor armorname ?>
+Shield shieldname ?>
+Slot 1: slot1name ?>
+Slot 2: slot2name ?>
+Slot 3: slot3name ?>
Spells