From 5235239c1cef287286fb1ccff6f586f1f2c6c564 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Fri, 13 Dec 2024 14:57:48 -0600 Subject: [PATCH] Completely move functions to new routing and namespaces --- public/admin.php | 2 +- public/forum.php | 2 +- public/help.php | 568 -------------------------------------- public/index.php | 71 ++--- public/login.php | 55 ---- src/fight.php | 15 + src/help.php | 500 +++++++++++++++++++++++++++++++++ src/lib.php | 39 ++- src/towns.php | 19 ++ {public => src}/users.php | 88 ++++-- templates/help.php | 85 ++++++ templates/leftnav.php | 4 +- templates/login.php | 2 +- templates/register.php | 2 +- 14 files changed, 760 insertions(+), 692 deletions(-) delete mode 100644 public/help.php delete mode 100644 public/login.php create mode 100644 src/help.php rename {public => src}/users.php (68%) create mode 100644 templates/help.php diff --git a/public/admin.php b/public/admin.php index d2d1a7b..32dbb86 100644 --- a/public/admin.php +++ b/public/admin.php @@ -11,7 +11,7 @@ $controlrow = get_control_row(); // Login (or verify) if not logged in. if (($userrow = checkcookies()) === false) { if (isset($_GET['do']) && $_GET['do'] === 'verify') redirect('users.php?do=verify'); - redirect('login.php?do=login'); + redirect('/login'); } if ($userrow["authlevel"] !== 1) exit("You must have administrator privileges to use the control panel."); diff --git a/public/forum.php b/public/forum.php index 1c7f1aa..976408a 100644 --- a/public/forum.php +++ b/public/forum.php @@ -15,7 +15,7 @@ if (($userrow = checkcookies()) === false) { exit; } - header("Location: login.php?do=login"); + header("Location: /login"); exit; } diff --git a/public/help.php b/public/help.php deleted file mode 100644 index 27591f3..0000000 --- a/public/help.php +++ /dev/null @@ -1,568 +0,0 @@ - - - - - - <?= $controlrow["gamename"] ?> Help - - - - -

Help

- [ Return to the game ] - -


- - -

Table of Contents

- - -
- -

Introduction

- Firstly, I'd like to say thank you for playing my game. The Dragon Knight game engine is the result of several months of - planning, coding and testing. The original idea was to create a web-based tribute to the NES game, Dragon - Warrior. In its current iteration, only the underlying fighting system really resembles that game, as almost - everything else in DK has been made bigger and better. But you should still recognize bits and pieces as stemming - from Dragon Warrior and other RPGs of old.

- This is the first game I've ever written, and it has definitely been a positive experience. It got difficult at - times, admittedly, but it was still a lot of fun to write, and even more fun to play. And I hope to use this - experience so that if I ever want to create another game it will be even better than this one.

- If you are a site administrator, and would like to install a copy of DK on your own server, you may visit the - development site for Dragon Knight. This page - includes the downloadable game souce code, as well as some other resources that developers and administrators may - find valuable.

- Once again, thanks for playing!

- Jamin Seven
- Dragon Knight creator
- My Homepage
- Dragon Knight Homepage

- [ Top ] - -


- -

Character Classes

- There are three character classes in the game. The main differences between the classes are what spells you get - access to, the speed with which you level up, and the amount of HP/MP/strength/dexterity you gain per level. Below - is a basic outline of each of the character classes. For more detailed information about the characters, please - view the Levels table at the bottom of this page. Also, note that the outline below refers to the stock class setup - for the game. If your administrator has used his/her own class setup, this information may not be accurate.

- - - - - - - [ Top ] - -


- -

Playing The Game: In Town

- When you begin a new game, the first thing you see is the Town screen. Towns serve four primary functions: healing, buying items, - buying maps, and displaying game information.

- To heal yourself, click the "Rest at the Inn" link at the top of the town screen. Each town's Inn has a different price - some towns - are cheap, others are expensive. No matter what town you're in, the Inns always serve the same function: they restore your current - hit points, magic points, and travel points to their maximum amounts. Out in the field, you are free to use healing spells to restore - your hit points, but when you run low on magic points, the only way to restore them is at an Inn.

- Buying weapons and armor is accomplished through the appropriately-named "Buy Weapons/Armor" link. Not every item is available in - every town, so in order to get the most powerful items, you'll need to explore some of the outer towns. Once you've clicked the link, - you are presented with a list of items available in this town's store. To the left of each item is an icon that represents its type: - weapon, armor or shield. The amount of attack/defense power, as well as the item's price, are displayed to the right of the item name. - You'll notice that some items have a red asterisk (*) next to their names. These are items that come - with special attributes that modify other parts of your character profile. See the Items & Drops table at the bottom of this page for - more information about special items.

- Maps are the third function in towns. Buying a map to a town places the town in your Travel To box in the left status panel. Once - you've purchased a town's map, you can click its name from your Travel To box and you will jump to that town. Travelling this way - costs travel points, though, and you'll only be able to visit towns if you have enough travel points.

- The final function in towns is displaying game information and statistics. This includes the latest news post made by the game - administrator, a list of players who have been online recently, and the Babble Box.

- [ Top ] - -


- -

Playing The Game: Exploring & Fighting

- Once you're done in town, you are free to start exploring the world. Use the compass buttons on the left status panel to move around. - The game world is basically a big square, divided into four quadrants. Each quadrant is spaces - square. The first town is usually located at (0N,0E). Click the North button from the first town, and now you'll be at (1N,0E). - Likewise, if you now click the West button, you'll be at (1N,1W). Monster levels increase with every 5 spaces you move outward - from (0N,0E).

- While you're exploring, you will occasionally run into monsters. As in pretty much any other RPG game, you and the monster take turns - hitting each other in an attempt to reduce each other's hit points to zero. Once you run into a monster, the Exploring screen changes - to the Fighting screen.

- When a fight begins, you'll see the monster's name and hit points, and the game will ask you for your first command. You then get to - pick whether you want to fight, use a spell, or run away. Note, though, that sometimes the monster has the chance to hit you - first.

- The Fight button is pretty straightforward: you attack the monster, and the amount of damage dealt is based on your attack power and - the monster's armor. On top of that, there are two other things that can happen: an Excellent Hit, which doubles your total attack - damage; and a monster dodge, which results in you doing no damage to the monster.

- The Spell button allows you to pick an available spell and cast it. See the Spells list at the bottom of this page for more information - about spells.

- Finally, there is the Run button, which lets you run away from a fight if the monster is too powerful. Be warned, though: it is - possible for the monster to block you from running and attack you. So if your hit points are low, you may fare better by staying - around monsters that you know can't do much damage to you.

- Once you've had your turn, the monster also gets his turn. It is also possible for you to dodge the monster's attack and take no - damage.

- The end result of a fight is either you or the monster being knocked down to zero hit points. If you win, the monster dies and will - give you a certain amount of experience and gold. There is also a chance that the monster will drop an item, which you can put into - one of the three inventory slots to give you extra points in your character profile. If you lose and die, half of your gold is taken - away - however, you are given back a few hit points to help you make it back to town (for example, if you don't have enough gold to - pay for an Inn, and need to kill a couple low-level monsters to get the money).

- When the fight is over, you can continue exploring until you find another monster to beat into submission.

- [ Top ] - -


- -

Playing The Game: Status Panels

- There are two status panels on the game screen: left and right.

- The left panel inclues your current location and play status (In Town, Exploring, Fighting), compass buttons for movement, and the - Travel To list for jumping between towns. At the bottom of the left panel is also a list of game functions.

- The right panel displays some character statistics, your inventory, and quick spells.

- The Character section shows the most important character statistics. It also displays the status bars for your current hit points, - magic points and travel points. These status bars are colored either green, yellow or red depending on your current amount of each - stat. There is also a link to pop up your list of extended statistics, which shows more detailed character information.

- The Fast Spells section lists any Heal spells you've learned. You may use these links any time you are in town or exploring to cast - the heal spell. These may not be used during fights, however - you have to use the Spells box on the fight screen for that. - [ Top ] - -


- -

Spoilers: Items & Drops

- Click here for the Items & Drops spoiler page.

- [ Top ] - -


- -

Spoilers: Monsters

- Click here for the Monsters spoiler page.

- [ Top ] - -


- -

Spoilers: Spells

- Click here for the Spells spoiler page.

- [ Top ] - -


- -

Spoilers: Levels

- Click here for the Levels spoiler page.

- [ Top ] - -


- -

Credits

- - Apologies and lots of happy naked love to anyone I forgot.

- And of course, thanks to you for playing my game!

- NINJA!

- [ Top ] - -



- - Please visit the following sites for more information:
- Se7enet (Jamin's homepage)
- Dragon Knight (official DK homepage)
- Forums (official DK forums)

- All original coding and graphics for the Dragon Knight game engine are © 2003-2005 by Jamin Seven.

- [ Top ] - - - - - - - - - query('SELECT * FROM items ORDER BY id;'); - $item_types = [1 => ['weapon', 'Attack'], 2 => ['armor', 'Defense'], 3 => ['shield', 'Defense']]; - - while ($item = $items->fetchArray(SQLITE3_ASSOC)) { - $image = $item_types[$item['type']][0]; - $power = $item_types[$item['type']][1]; - - if ($item['special'] !== 'X') { - $special = explode(',', $item['special']); - $attr = special_to_string($special[0]); - $stat = (($special[1] > 0) ? '+' : '') . $special[1]; - $bigspecial = "$attr $stat"; - } else { - $bigspecial = 'None'; - } - - echo "\n"; - } - ?> -
Items
TypeNameCostAttributeSpecial
\"$image\"".$item["name"]."".$item["buycost"]." Gold".$item["attribute"]." $power Power$bigspecial
- -

- - - - - query('SELECT * FROM drops ORDER BY id;'); - - while ($drop = $drops->fetchArray(SQLITE3_ASSOC)) { - if ($drop["attribute1"] !== "X") { - $special = explode(',', $drop['attribute1']); - $attr = special_to_string($special[0]); - $stat = (($special[1] > 0) ? '+' : '') . $special[1]; - $bigspecial1 = "$attr $stat"; - } else { - $bigspecial1 = 'None'; - } - - if ($drop["attribute2"] !== "X") { - $special = explode(',', $drop['attribute2']); - $attr = special_to_string($special[0]); - $stat = (($special[1] > 0) ? '+' : '') . $special[1]; - $bigspecial2 = "$attr $stat"; - } else { - $bigspecial2 = 'None'; - } - - echo "\n"; - } - ?> -
Drops
NameMonster LevelAttribute 1Attribute 2
".$drop["name"]."".$drop["mlevel"]."$bigspecial1$bigspecial2
- - - - - - - - - query('SELECT * FROM spells ORDER BY id;'); - $spell_types = ['None', 'Heal', 'Hurt', 'Sleep', '+Damage (%)', '+Defense (%)']; - while ($spell = $spells->fetchArray(SQLITE3_ASSOC)) { - echo << - - - - HTML; - } - ?> -
Spells
NameCostTypeAttribute
{$spell["name"]}{$spell["mp"]}{$spell_types[$spell['type']]}{$spell["attribute"]}
- - - - - - - - - - query('SELECT * FROM monsters ORDER BY id;'); - $immunities = ['None', 'Hurt', 'Hurt & Sleep']; - - while ($m = $monsters->fetchArray(SQLITE3_ASSOC)) { - $immune = $immunities[$m['immune']] ?? 'Unknown'; - echo "\n"; - } - ?> -
Monsters
NameMax HPMax DamageArmorLevelMax Exp.Max GoldImmunity
".$m["name"]."".$m["maxhp"]."".$m["maxdam"]."".$m["armor"]."".$m["level"]."".$m["maxexp"]."".$m["maxgold"]."$immune
- - - - query('SELECT * FROM levels ORDER BY id;'); - while ($level = $levels->fetchArray(SQLITE3_ASSOC)) { - $class_data = [1 => [], 2 => [], 3 => []]; - - foreach ($level as $column => $value) { - if ($column === 'id') continue; - - $parts = explode('_', $column); - $class_number = (int) $parts[0]; - $attribute = $parts[1]; - - if (in_array($class_number, [1, 2, 3])) { - $class_data[$class_number][$level['id']][$attribute] = $value; - } - } - - $rows[$level['id']] = $class_data; - } - - $spells = []; - $spells_query = db()->query('SELECT * FROM spells ORDER BY id;'); - while ($spell = $spells_query->fetchArray(SQLITE3_ASSOC)) { - $spells[$spell['id']] = $spell; - } - ?> - - - - - $class_data) { - $level_data = $class_data[1][$level]; - - $spell_name = ''; - if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - ?> -
Levels
LevelExp.HPMPTPStrengthDexteritySpell
' . $level . '' . ($level_data['exp'] ?? '') . '' . ($level_data['hp'] ?? '') . '' . ($level_data['mp'] ?? '') . '' . ($level_data['tp'] ?? '') . '' . ($level_data['strength'] ?? '') . '' . ($level_data['dexterity'] ?? '') . '' . (!empty($spell_name) ? $spell_name : 'None') . '
- -

- - - - - $class_data) { - $level_data = $class_data[2][$level]; - - $spell_name = ''; - if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - ?> -
Levels
LevelExp.HPMPTPStrengthDexteritySpell
' . $level . '' . ($level_data['exp'] ?? '') . '' . ($level_data['hp'] ?? '') . '' . ($level_data['mp'] ?? '') . '' . ($level_data['tp'] ?? '') . '' . ($level_data['strength'] ?? '') . '' . ($level_data['dexterity'] ?? '') . '' . (!empty($spell_name) ? $spell_name : 'None') . '
- -

- - - - - $class_data) { - $level_data = $class_data[3][$level]; - - $spell_name = ''; - if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; - - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - echo ''; - } - ?> -
Levels
LevelExp.HPMPTPStrengthDexteritySpell
' . $level . '' . ($level_data['exp'] ?? '') . '' . ($level_data['hp'] ?? '') . '' . ($level_data['mp'] ?? '') . '' . ($level_data['tp'] ?? '') . '' . ($level_data['strength'] ?? '') . '' . ($level_data['dexterity'] ?? '') . '' . (!empty($spell_name) ? $spell_name : 'None') . '
- -
- - Experience points listed are total values up until that point. All other values are just the new amount that you gain for each level. - - - -

- - - - - - diff --git a/public/index.php b/public/index.php index 0a4fd2b..d15b626 100644 --- a/public/index.php +++ b/public/index.php @@ -4,37 +4,42 @@ require_once '../src/lib.php'; require_once '../src/router.php'; +require_once '../src/users.php'; +require_once '../src/help.php'; if (!file_exists('../.installed')) redirect('install.php'); $controlrow = get_control_row(); -// Login (or verify) if not logged in. -if (($userrow = checkcookies()) === false) { - if (isset($_GET['do']) && $_GET['do'] === 'verify') { - header("Location: users.php?do=verify"); - exit; - } - - header("Location: login.php?do=login"); - exit; -} - -// Close game. -if ((bool) $controlrow["gameopen"] === false) { +if (!$controlrow["gameopen"]) { display("The game is currently closed for maintanence. Please check back later.", "Game Closed"); exit; } -// Force verify if the user isn't verified yet. -if ((bool) $controlrow["verifyemail"] && (bool) $userrow["verify"] === false) { - header("Location: users.php?do=verify"); - exit; +// Login (or verify) if not logged in. +if (($userrow = checkcookies()) === false) { + $uri = uri(); + if (!in_array($uri[0], ['login', 'register', 'verify', 'lostpassword', 'help'])) { + redirect('/login'); + } +} else { + // Block user if he/she has been banned. + if ($userrow["authlevel"] === 2) { + exit("Your account has been banned."); + } + + // Force verify if the user isn't verified yet. + if ($controlrow["verifyemail"] && (bool) $userrow["verify"] === false) { + redirect('/verify'); + header("Location: users.php?do=verify"); + exit; + } } -// Block user if he/she has been banned. -if ($userrow["authlevel"] === 2) { - exit("Your account has been blocked."); +// Close game. +if (!$controlrow["gameopen"]) { + display("The game is currently closed for maintanence. Please check back later.", "Game Closed"); + exit; } require_once '../src/towns.php'; @@ -58,35 +63,19 @@ $r->get('/', function() { } display($page, $title); -}); +})->middleware('auth_only'); $r->get('/ninja', function() { exit('NINJA! 🥷'); }); -$r->get('/inn', 'inn'); -$r->post('/inn', 'inn'); -$r->get('/buy', 'buy'); -$r->get('/buy2/:id', 'buy2'); -$r->post('/buy3/:id', 'buy3'); -// $r->get('/sell', 'sell'); -$r->get('/maps', 'maps'); -$r->get('/maps2/:id', 'maps2'); -$r->post('/maps3/:id', 'maps3'); -$r->get('/gotown/:id', 'travelto'); +Towns\register_routes($r); +Fights\register_routes($r); +Users\register_routes($r); +Help\register_routes($r); $r->post('/move', 'move'); -$r->get('/fight', 'fight'); -$r->post('/fight', 'fight'); -$r->get('/victory', 'victory'); -$r->get('/drop', 'drop'); -$r->post('/drop', 'drop'); -$r->get('/dead', 'dead'); - -$r->get('/verify', function() { - redirect('users.php?do=verify'); -}); $r->get('/spell/:id', 'healspells'); $r->get('/showchar', 'showchar'); $r->get('/onlinechar/:id', 'onlinechar'); diff --git a/public/login.php b/public/login.php deleted file mode 100644 index 8c90dfa..0000000 --- a/public/login.php +++ /dev/null @@ -1,55 +0,0 @@ - login(), - 'logout' => logout() -}; - -function login() -{ - if (checkcookies() !== false) redirect('/'); - - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $form = validate($_POST, [ - 'username' => ['length:3-18', 'alpha-spaces'], - 'password' => ['length:6-255'], - 'remember' => ['bool'] - ]); - - if (!$form['valid']) { - exit(ul_from_validate_errors($form['errors'])); - } - - $form = $form['data']; - - $query = db()->query('SELECT id, username, password FROM users WHERE username = ? COLLATE NOCASE LIMIT 1;', [$form['username']]); - $row = $query ? $query->fetchArray(SQLITE3_ASSOC) : false; - - if ($row === false || !password_verify($_POST['password'] ?? '', $row['password'])) - die("Invalid username or password. Please go back and try again."); - - $expiretime = $form['remember'] ? time() + 31536000 : 0; - $rememberme = $form['remember'] ? 1 : 0; - $cookie = implode(' ', [$row['id'], $row['username'], $row['password'], $rememberme]); - - set_cookie("dkgame", $cookie, $expiretime); - redirect('/'); - } - - $page = gettemplate("login"); - $title = "Log In"; - display($page, $title, false, false, false, false); -} - - -function logout() -{ - set_cookie("dkgame", "", -3600); - redirect('login.php?do=login'); -} diff --git a/src/fight.php b/src/fight.php index 3114a70..0c58e1c 100644 --- a/src/fight.php +++ b/src/fight.php @@ -2,6 +2,21 @@ // fight.php :: Handles all fighting action. +namespace Fights; + +use Router; + +function register_routes(Router $r): Router +{ + $r->get('/fight', 'Fights\fight'); + $r->post('/fight', 'Fights\fight'); + $r->get('/victory', 'Fights\victory'); + $r->get('/drop', 'Fights\drop'); + $r->post('/drop', 'Fights\drop'); + $r->get('/dead', 'Fights\dead'); + return $r; +} + /** * One big long function that determines the outcome of the fight. */ diff --git a/src/help.php b/src/help.php new file mode 100644 index 0000000..741c9e7 --- /dev/null +++ b/src/help.php @@ -0,0 +1,500 @@ +get('/help', 'Help\main'); + $r->get('/help/items', 'Help\items'); + $r->get('/help/spells', 'Help\spells'); + $r->get('/help/monsters', 'Help\monsters'); + $r->get('/help/levels', 'Help\levels'); + return $r; +} + +function main() +{ + global $controlrow; + + $page = <<Table of Contents + + +
+ +

Introduction

+ Firstly, I'd like to say thank you for playing my game. The Dragon Knight game engine is the result of several months of + planning, coding and testing. The original idea was to create a web-based tribute to the NES game, Dragon + Warrior. In its current iteration, only the underlying fighting system really resembles that game, as almost + everything else in DK has been made bigger and better. But you should still recognize bits and pieces as stemming + from Dragon Warrior and other RPGs of old.

+ This is the first game I've ever written, and it has definitely been a positive experience. It got difficult at + times, admittedly, but it was still a lot of fun to write, and even more fun to play. And I hope to use this + experience so that if I ever want to create another game it will be even better than this one.

+ If you are a site administrator, and would like to install a copy of DK on your own server, you may visit the + development site for Dragon Knight. This page + includes the downloadable game souce code, as well as some other resources that developers and administrators may + find valuable.

+ Once again, thanks for playing!

+ Jamin Seven
+ Dragon Knight creator
+ My Homepage
+ Dragon Knight Homepage

+ [ Top ] + +


+ +

Character Classes

+ There are three character classes in the game. The main differences between the classes are what spells you get + access to, the speed with which you level up, and the amount of HP/MP/strength/dexterity you gain per level. Below + is a basic outline of each of the character classes. For more detailed information about the characters, please + view the Levels table at the bottom of this page. Also, note that the outline below refers to the stock class setup + for the game. If your administrator has used his/her own class setup, this information may not be accurate.

+ {{class1name}} + + {{class2name}} + + {{class3name}} + + [ Top ] + +


+ +

Playing The Game: In Town

+ When you begin a new game, the first thing you see is the Town screen. Towns serve four primary functions: healing, buying items, + buying maps, and displaying game information.

+ To heal yourself, click the "Rest at the Inn" link at the top of the town screen. Each town's Inn has a different price - some towns + are cheap, others are expensive. No matter what town you're in, the Inns always serve the same function: they restore your current + hit points, magic points, and travel points to their maximum amounts. Out in the field, you are free to use healing spells to restore + your hit points, but when you run low on magic points, the only way to restore them is at an Inn.

+ Buying weapons and armor is accomplished through the appropriately-named "Buy Weapons/Armor" link. Not every item is available in + every town, so in order to get the most powerful items, you'll need to explore some of the outer towns. Once you've clicked the link, + you are presented with a list of items available in this town's store. To the left of each item is an icon that represents its type: + weapon, armor or shield. The amount of attack/defense power, as well as the item's price, are displayed to the right of the item name. + You'll notice that some items have a red asterisk (*) next to their names. These are items that come + with special attributes that modify other parts of your character profile. See the Items & Drops table at the bottom of this page for + more information about special items.

+ Maps are the third function in towns. Buying a map to a town places the town in your Travel To box in the left status panel. Once + you've purchased a town's map, you can click its name from your Travel To box and you will jump to that town. Travelling this way + costs travel points, though, and you'll only be able to visit towns if you have enough travel points.

+ The final function in towns is displaying game information and statistics. This includes the latest news post made by the game + administrator, a list of players who have been online recently, and the Babble Box.

+ [ Top ] + +


+ +

Playing The Game: Exploring & Fighting

+ Once you're done in town, you are free to start exploring the world. Use the compass buttons on the left status panel to move around. + The game world is basically a big square, divided into four quadrants. Each quadrant is {{gamesize}} spaces + square. The first town is usually located at (0N,0E). Click the North button from the first town, and now you'll be at (1N,0E). + Likewise, if you now click the West button, you'll be at (1N,1W). Monster levels increase with every 5 spaces you move outward + from (0N,0E).

+ While you're exploring, you will occasionally run into monsters. As in pretty much any other RPG game, you and the monster take turns + hitting each other in an attempt to reduce each other's hit points to zero. Once you run into a monster, the Exploring screen changes + to the Fighting screen.

+ When a fight begins, you'll see the monster's name and hit points, and the game will ask you for your first command. You then get to + pick whether you want to fight, use a spell, or run away. Note, though, that sometimes the monster has the chance to hit you + first.

+ The Fight button is pretty straightforward: you attack the monster, and the amount of damage dealt is based on your attack power and + the monster's armor. On top of that, there are two other things that can happen: an Excellent Hit, which doubles your total attack + damage; and a monster dodge, which results in you doing no damage to the monster.

+ The Spell button allows you to pick an available spell and cast it. See the Spells list at the bottom of this page for more information + about spells.

+ Finally, there is the Run button, which lets you run away from a fight if the monster is too powerful. Be warned, though: it is + possible for the monster to block you from running and attack you. So if your hit points are low, you may fare better by staying + around monsters that you know can't do much damage to you.

+ Once you've had your turn, the monster also gets his turn. It is also possible for you to dodge the monster's attack and take no + damage.

+ The end result of a fight is either you or the monster being knocked down to zero hit points. If you win, the monster dies and will + give you a certain amount of experience and gold. There is also a chance that the monster will drop an item, which you can put into + one of the three inventory slots to give you extra points in your character profile. If you lose and die, half of your gold is taken + away - however, you are given back a few hit points to help you make it back to town (for example, if you don't have enough gold to + pay for an Inn, and need to kill a couple low-level monsters to get the money).

+ When the fight is over, you can continue exploring until you find another monster to beat into submission.

+ [ Top ] + +


+ +

Playing The Game: Status Panels

+ There are two status panels on the game screen: left and right.

+ The left panel inclues your current location and play status (In Town, Exploring, Fighting), compass buttons for movement, and the + Travel To list for jumping between towns. At the bottom of the left panel is also a list of game functions.

+ The right panel displays some character statistics, your inventory, and quick spells.

+ The Character section shows the most important character statistics. It also displays the status bars for your current hit points, + magic points and travel points. These status bars are colored either green, yellow or red depending on your current amount of each + stat. There is also a link to pop up your list of extended statistics, which shows more detailed character information.

+ The Fast Spells section lists any Heal spells you've learned. You may use these links any time you are in town or exploring to cast + the heal spell. These may not be used during fights, however - you have to use the Spells box on the fight screen for that. + [ Top ] + +


+ +

Spoilers: Items & Drops

+ Click here for the Items & Drops spoiler page.

+ [ Top ] + +


+ +

Spoilers: Monsters

+ Click here for the Monsters spoiler page.

+ [ Top ] + +


+ +

Spoilers: Spells

+ Click here for the Spells spoiler page.

+ [ Top ] + +


+ +

Spoilers: Levels

+ Click here for the Levels spoiler page.

+ [ Top ] + +


+ +

Credits

+ + Apologies and lots of happy naked love to anyone I forgot.

+ And of course, thanks to you for playing my game!

+ NINJA!

+ [ Top ] + +



+ + Please visit the following sites for more information:
+ Se7enet (Jamin's homepage)
+ Dragon Knight (official DK homepage)
+ Forums (official DK forums)

+ All original coding and graphics for the Dragon Knight game engine are © 2003-2005 by Jamin Seven.

+ [ Top ] + HTML; + + display_help(parsetemplate($page, $controlrow)); +} + +function items() +{ + global $controlrow; + + $page = << +
Items
+ TypeNameCostAttributeSpecial + HTML; + + $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)) { + $image = $item_types[$item['type']][0]; + $power = $item_types[$item['type']][1]; + + if ($item['special'] !== 'X') { + $special = explode(',', $item['special']); + $attr = special_to_string($special[0]); + $stat = (($special[1] > 0) ? '+' : '') . $special[1]; + $bigspecial = "$attr $stat"; + } else { + $bigspecial = 'None'; + } + + $page .= "\"$image\"".$item["name"]."".$item["buycost"]." Gold".$item["attribute"]." $power Power$bigspecial\n"; + } + + $page .= << + +

+ + + + + HTML; + + $drops = db()->query('SELECT * FROM drops ORDER BY id;'); + + while ($drop = $drops->fetchArray(SQLITE3_ASSOC)) { + if ($drop["attribute1"] !== "X") { + $special = explode(',', $drop['attribute1']); + $attr = special_to_string($special[0]); + $stat = (($special[1] > 0) ? '+' : '') . $special[1]; + $bigspecial1 = "$attr $stat"; + } else { + $bigspecial1 = 'None'; + } + + if ($drop["attribute2"] !== "X") { + $special = explode(',', $drop['attribute2']); + $attr = special_to_string($special[0]); + $stat = (($special[1] > 0) ? '+' : '') . $special[1]; + $bigspecial2 = "$attr $stat"; + } else { + $bigspecial2 = 'None'; + } + + $page .= "\n"; + } + $page .= '
Drops
NameMonster LevelAttribute 1Attribute 2
".$drop["name"]."".$drop["mlevel"]."$bigspecial1$bigspecial2
'; + + display_help($page); +} + + +function spells() +{ + global $controlrow; + + $page = << +
Spells
+ NameCostTypeAttribute + HTML; + + $spells = db()->query('SELECT * FROM spells ORDER BY id;'); + $spell_types = ['None', 'Heal', 'Hurt', 'Sleep', '+Damage (%)', '+Defense (%)']; + while ($spell = $spells->fetchArray(SQLITE3_ASSOC)) { + $page .= <<{$spell["name"]} + {$spell["mp"]} + {$spell_types[$spell['type']]} + {$spell["attribute"]} + HTML; + } + + $page .= << + + HTML; + + display_help($page); +} + +function monsters() +{ + $page = << +
Monsters
+ NameMax HPMax DamageArmorLevelMax Exp.Max GoldImmunity + HTML; + + $monsters = db()->query('SELECT * FROM monsters ORDER BY id;'); + $immunities = ['None', 'Hurt', 'Hurt & Sleep']; + + while ($m = $monsters->fetchArray(SQLITE3_ASSOC)) { + $immune = $immunities[$m['immune']] ?? 'Unknown'; + $page .= "".$m["name"]."".$m["maxhp"]."".$m["maxdam"]."".$m["armor"]."".$m["level"]."".$m["maxexp"]."".$m["maxgold"]."$immune\n"; + } + + display_help($page.''); +} + +function levels() +{ + global $controlrow; + + $rows = []; + + $levels = db()->query('SELECT * FROM levels ORDER BY id;'); + while ($level = $levels->fetchArray(SQLITE3_ASSOC)) { + $class_data = [1 => [], 2 => [], 3 => []]; + + foreach ($level as $column => $value) { + if ($column === 'id') continue; + + $parts = explode('_', $column); + $class_number = (int) $parts[0]; + $attribute = $parts[1]; + + if (in_array($class_number, [1, 2, 3])) { + $class_data[$class_number][$level['id']][$attribute] = $value; + } + } + + $rows[$level['id']] = $class_data; + } + + $spells = []; + $spells_query = db()->query('SELECT * FROM spells ORDER BY id;'); + while ($spell = $spells_query->fetchArray(SQLITE3_ASSOC)) { + $spells[$spell['id']] = $spell; + } + + $page = << +
{{class1name}} Levels
+ LevelExp.HPMPTPStrengthDexteritySpell + HTML; + foreach ($rows as $level => $class_data) { + $level_data = $class_data[1][$level]; + + $spell_name = ''; + if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; + + $page .= ''; + $page .= '' . $level . ''; + $page .= '' . ($level_data['exp'] ?? '') . ''; + $page .= '' . ($level_data['hp'] ?? '') . ''; + $page .= '' . ($level_data['mp'] ?? '') . ''; + $page .= '' . ($level_data['tp'] ?? '') . ''; + $page .= '' . ($level_data['strength'] ?? '') . ''; + $page .= '' . ($level_data['dexterity'] ?? '') . ''; + $page .= '' . (!empty($spell_name) ? $spell_name : 'None') . ''; + $page .= ''; + } + + $page .= << + +

+ + + + + HTML; + + foreach ($rows as $level => $class_data) { + $level_data = $class_data[2][$level]; + + $spell_name = ''; + if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; + + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + } + + $page .= << + +

+ +
{{class2name}} Levels
LevelExp.HPMPTPStrengthDexteritySpell
' . $level . '' . ($level_data['exp'] ?? '') . '' . ($level_data['hp'] ?? '') . '' . ($level_data['mp'] ?? '') . '' . ($level_data['tp'] ?? '') . '' . ($level_data['strength'] ?? '') . '' . ($level_data['dexterity'] ?? '') . '' . (!empty($spell_name) ? $spell_name : 'None') . '
+ + + HTML; + + foreach ($rows as $level => $class_data) { + $level_data = $class_data[3][$level]; + + $spell_name = ''; + if (!empty($level_data['spells'])) $spell_name = $spells[$level_data['spells']]['name']; + + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + $page .= ''; + } + + $page .= << + +
+ + Experience points listed are total values up until that point. All other values are just the new amount that you gain for each level. + HTML; + + display_help(parsetemplate($page, $controlrow)); +} + +function display_help(string $content) +{ + global $controlrow; + $template = parsetemplate(gettemplate('help'), $controlrow); + echo parsetemplate($template, ['content' => $content]); +} diff --git a/src/lib.php b/src/lib.php index 85b4b3a..c1af9f3 100644 --- a/src/lib.php +++ b/src/lib.php @@ -96,7 +96,7 @@ function admindisplay($content, $title) function display($content, $title, $topnav=true, $leftnav=true, $rightnav=true, $badstart=false) { // Finalize page and output to browser. - global $numqueries, $userrow, $controlrow, $version, $build; + global $userrow, $controlrow; if (!isset($controlrow)) { $query = db()->query('SELECT * FROM control WHERE id=1 LIMIT 1;'); $controlrow = $query->fetchArray(SQLITE3_ASSOC); @@ -106,12 +106,19 @@ function display($content, $title, $topnav=true, $leftnav=true, $rightnav=true, if ($rightnav == true) { $rightnav = gettemplate("rightnav"); } else { $rightnav = ""; } if ($leftnav == true) { $leftnav = gettemplate("leftnav"); } else { $leftnav = ""; } if ($topnav == true) { - $topnav = "\"Log\"Help\""; + $topnav = <<Log Out + Help + HTML; } else { - $topnav = "\"Log\"Register\"\"Help\""; + $topnav = <<Log In + Register + Help + HTML; } - if (isset($userrow)) { + if (isset($userrow) && $userrow !== false) { // Get userrow again, in case something has been updated. $userquery = db()->query('SELECT * FROM users WHERE id = ? LIMIT 1;', [$userrow['id']]); @@ -526,3 +533,27 @@ function ul_from_validate_errors(array $errors): string } return $string . ''; } + +/** + * Get the URI, broken up into chunks. + */ +function uri(): array +{ + return explode('/', trim($_SERVER['REQUEST_URI'], '/')); +} + +/** + * Redirect to login if not authenticated. + */ +function auth_only(): void +{ + if (!checkcookies()) redirect('/login'); +} + +/** + * Redirect to home if authenticated. + */ +function guest_only(): void +{ + if (checkcookies()) redirect('/login'); +} diff --git a/src/towns.php b/src/towns.php index 9b24345..6383507 100644 --- a/src/towns.php +++ b/src/towns.php @@ -2,6 +2,25 @@ // towns.php :: Handles all actions you can do in town. +namespace Towns; + +use Router; + +function register_routes(Router $r): Router +{ + $r->get('/inn', 'Towns\inn'); + $r->post('/inn', 'Towns\inn'); + $r->get('/buy', 'Towns\buy'); + $r->get('/buy2/:id', 'Towns\buy2'); + $r->post('/buy3/:id', 'Towns\buy3'); + // $r->get('/sell', 'Towns\sell'); + $r->get('/maps', 'Towns\maps'); + $r->get('/maps2/:id', 'Towns\maps2'); + $r->post('/maps3/:id', 'Towns\maps3'); + $r->get('/gotown/:id', 'Towns\travelto'); + return $r; +} + /** * Staying at the inn resets all expendable stats to their max values. */ diff --git a/public/users.php b/src/users.php similarity index 68% rename from public/users.php rename to src/users.php index 29d8120..d90c731 100644 --- a/public/users.php +++ b/src/users.php @@ -1,20 +1,72 @@ get('/login', 'Users\login'); + $r->post('/login', 'Users\login'); + $r->get('/logout', 'Users\logout'); + $r->get('/register', 'Users\register'); + $r->post('/register', 'Users\register'); + $r->get('/lostpassword', 'Users\lostpassword'); + $r->post('/lostpassword', 'Users\lostpassword'); + $r->get('/changepassword', 'Users\changepassword'); + $r->post('/changepassword', 'Users\changepassword'); + $r->get('/verify', 'Users\verify'); + $r->post('/verify', 'Users\verify'); + return $r; +} -$controlrow = get_control_row(); +/** + * Displays the login page, and processes login requests. + */ +function login() +{ + if (checkcookies() !== false) redirect('/'); -$do = $_GET['do'] ?? 'register'; -match ($do) { - 'verify' => verify(), - 'lostpassword' => lostpassword(), - 'changepassword' => changepassword(), - default => register() -}; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $form = validate($_POST, [ + 'username' => ['length:3-18', 'alpha-spaces'], + 'password' => ['length:6-255'], + 'remember' => ['bool'] + ]); + + if (!$form['valid']) { + exit(ul_from_validate_errors($form['errors'])); + } + + $form = $form['data']; + + $query = db()->query('SELECT id, username, password FROM users WHERE username = ? COLLATE NOCASE LIMIT 1;', [$form['username']]); + $row = $query ? $query->fetchArray(SQLITE3_ASSOC) : false; + + if ($row === false || !password_verify($_POST['password'] ?? '', $row['password'])) + die("Invalid username or password. Please go back and try again."); + + $expiretime = $form['remember'] ? time() + 31536000 : 0; + $rememberme = $form['remember'] ? 1 : 0; + $cookie = implode(' ', [$row['id'], $row['username'], $row['password'], $rememberme]); + + set_cookie("dkgame", $cookie, $expiretime); + redirect('/'); + } + + $page = gettemplate("login"); + $title = "Log In"; + display($page, $title, false, false, false, false); +} + +/** + * Delete the current cookie and redirect to home. + */ +function logout() +{ + set_cookie("dkgame", "", -3600); + redirect('/login'); +} /** * Register a new account. @@ -51,7 +103,7 @@ function register() $page = "Your account was created successfully.

However, there was a problem sending your verification email. Please check with the game administrator to help resolve this problem."; } } else { - $page = "Your account was created succesfully.

You may now continue to the Login Page and continue playing ".$controlrow["gamename"]."!"; + $page = "Your account was created succesfully.

You may now continue to the Login Page and continue playing ".$controlrow["gamename"]."!"; } } } else { @@ -79,10 +131,10 @@ function verify() db()->query("UPDATE users SET verify='g2g' WHERE username=?;", [$u]); - display("Your account was verified successfully.

You may now continue to the Login Page and start playing the game.

Thanks for playing!","Verify Email",false,false,false); + display("Your account was verified successfully.

You may now continue to the Login Page and start playing the game.

Thanks for playing!","Verify Email",false,false,false); } - $topnav = "\"Log\"Register\"\"Help\""; + $topnav = "\"Log\"Register\"\"Help\""; display(gettemplate("verify"), "Verify Email", false, false, false); } @@ -99,13 +151,13 @@ function lostpassword() db()->query('UPDATE users SET password=? WHERE email=?;', [$hashed, $e]); if (sendpassemail($e, $newpass)) { - display("Your new password was emailed to the address you provided.

Once you receive it, you may Log In and continue playing.

Thank you.","Lost Password",false,false,false); + display("Your new password was emailed to the address you provided.

Once you receive it, you may Log In and continue playing.

Thank you.","Lost Password",false,false,false); } else { display("There was an error sending your new password.

Please check with the game administrator for more information.

We apologize for the inconvience.","Lost Password",false,false,false); } } - $topnav = "\"Log\"Register\"\"Help\""; + $topnav = "\"Log\"Register\"\"Help\""; display(gettemplate("lostpassword"), "Lost Password", false, false, false); } @@ -137,10 +189,10 @@ function changepassword() set_cookie('dkgame', '', -3600); - display("Your password was changed successfully.

You have been logged out of the game to avoid errors.

Please log back in to continue playing.","Change Password",false,false,false); + display("Your password was changed successfully.

You have been logged out of the game to avoid errors.

Please log back in to continue playing.","Change Password",false,false,false); } - $topnav = "\"Log\"Register\"\"Help\""; + $topnav = "\"Log\"Register\"\"Help\""; display(gettemplate("changepassword"), "Change Password", false, false, false); } diff --git a/templates/help.php b/templates/help.php new file mode 100644 index 0000000..8d69293 --- /dev/null +++ b/templates/help.php @@ -0,0 +1,85 @@ + + + + {{gamename}} Help + + + + +

{{gamename}} Help

+ [ Return to the game ] + +


+ + {{content}} + +

+ +
{{class3name}} Levels
LevelExp.HPMPTPStrengthDexteritySpell
' . $level . '' . ($level_data['exp'] ?? '') . '' . ($level_data['hp'] ?? '') . '' . ($level_data['mp'] ?? '') . '' . ($level_data['tp'] ?? '') . '' . ($level_data['strength'] ?? '') . '' . ($level_data['dexterity'] ?? '') . '' . (!empty($spell_name) ? $spell_name : 'None') . '
+ + + + +HTML; diff --git a/templates/leftnav.php b/templates/leftnav.php index 4e1e540..1b50df3 100644 --- a/templates/leftnav.php +++ b/templates/leftnav.php @@ -29,7 +29,7 @@ $template = <<Change Password
- Log Out
- Help + Log Out
+ Help HTML; diff --git a/templates/login.php b/templates/login.php index dbd5146..3fd02ea 100644 --- a/templates/login.php +++ b/templates/login.php @@ -1,7 +1,7 @@ +
diff --git a/templates/register.php b/templates/register.php index bdf2b02..ea40c97 100644 --- a/templates/register.php +++ b/templates/register.php @@ -8,7 +8,7 @@ $template = << - +
Username:Email Address:
Verify Email:{{verifytext}}


Character Class:
See Help for more information about character classes.

See Help for more information about character classes.