From 27f38f7ebcf284f2d8182774d18c1a7c5ebc3c02 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 18 Dec 2024 18:47:28 -0600 Subject: [PATCH] Update town behavior to HTMX, fix some redirect issues --- public/index.php | 1 + src/actions/towns.php | 238 +++++++++++++++++++--------------------- src/bootstrap.php | 2 +- src/lib.php | 12 +- templates/babblebox.php | 2 +- templates/towns.php | 6 +- 6 files changed, 118 insertions(+), 143 deletions(-) diff --git a/public/index.php b/public/index.php index e4b3e56..dc7b908 100644 --- a/public/index.php +++ b/public/index.php @@ -51,6 +51,7 @@ $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 .= ''.page_title().''; $content .= Render\debug_db_info(); if (env('debug', false)) $content .= Render\debug_query_log(); diff --git a/src/actions/towns.php b/src/actions/towns.php index 26a461b..b94c10b 100644 --- a/src/actions/towns.php +++ b/src/actions/towns.php @@ -9,9 +9,8 @@ use Router; function register_routes(Router $r): Router { $r->form('/inn', 'Towns\inn'); - $r->get('/buy', 'Towns\buy'); - $r->get('/buy2/:id', 'Towns\buy2'); - $r->post('/buy3/:id', 'Towns\buy3'); + $r->get('/shop', 'Towns\shop'); + $r->form('/buy/:id', 'Towns\buy'); // $r->get('/sell', 'Towns\sell'); $r->get('/maps', 'Towns\maps'); $r->get('/maps2/:id', 'Towns\maps2'); @@ -74,6 +73,7 @@ function town() /** * Staying at the inn resets all expendable stats to their max values. + * GET/POST /inn */ function inn() { @@ -88,21 +88,22 @@ 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 (isset($_POST['submit'])) { + } elseif ($_SERVER['REQUEST_METHOD'] === '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 (isset($_POST['cancel'])) { + } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && !$_POST['rest']) { redirect('/'); } else { $page = <<
A night's sleep at this Inn will cost you {$town['innprice']} gold. Is that ok?

- + +
HTML; } @@ -111,12 +112,14 @@ function inn() } /** - * Displays a list of available items for purchase. + * Displays a list of available items for purchase from the town the user is currently in. If the user is not in a town, + * redirects to home. + * GET /shop */ -function buy() +function shop() { $town = get_town_by_xy(user()->longitude, user()->latitude); - if ($town === false) { exit('Cheat attempt detected.

Get a life, loser.'); } + if ($town === false) exit('Cheat attempt detected.

Get a life, loser.'); $htmx = is_htmx(); page_title($town['name'] . ' Shop'); @@ -147,7 +150,7 @@ function buy() } else { $specialdot = $item['special'] !== 'X' ? '*' : ''; $page .= <<{$item['name']}$specialdot + {$item['name']}$specialdot $attrib {$item['attribute']} Price: {$item['buycost']} gold HTML; @@ -165,137 +168,116 @@ function buy() /** * Confirm user's intent to purchase item. */ -function buy2($id) +function buy(int $id) { - $townrow = get_town_by_xy(user()->longitude, user()->latitude); - if ($townrow === false) display("Cheat attempt detected.

Get a life, loser.", "Error"); - $townitems = explode(",", $townrow["itemslist"]); - if (!in_array($id, $townitems)) display("Cheat attempt detected.

Get a life, loser.", "Error"); - + $town = get_town_by_xy(user()->longitude, user()->latitude); + if ($town === false) redirect('/'); + if (!in_array($id, explode(',', $town['itemslist']))) redirect('/shop'); $item = get_item($id); + $can_afford = user()->gold >= $item['buycost']; - if (user()->gold < $item["buycost"]) { - display("You do not have enough gold to buy this item.

You may return to town, store, or use the direction buttons on the left to start exploring.", "Buy Items"); - } + if (!$can_afford) { + $page = <<{$item['name']}.

+ You may return to town, shop, + or use the direction buttons on the left to start exploring. + HTML; + } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && !$_POST['buy']) { + redirect('/shop'); + } elseif ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['buy']) { + $type_mapping = [ + 1 => ['id' => 'weaponid', 'name' => 'weaponname', 'power' => 'attackpower'], + 2 => ['id' => 'armorid', 'name' => 'armorname', 'power' => 'defensepower'], + 3 => ['id' => 'shieldid', 'name' => 'shieldname', 'power' => 'defensepower'] + ]; - $type_to_row_mapping = [1 => 'weaponid', 2 => 'armorid', 3 => 'shieldid']; - $current_equipped_id = user()[$type_to_row_mapping[$item['type']] ?? 0]; + if (!isset($type_mapping[$item["type"]])) { // should never happen + $page = 'Error! Invalid item type...
'.var_dump($item); + return is_htmx() ? $page : display($page, ''); + } - if ($current_equipped_id != 0) { - $item2 = get_item($current_equipped_id); - $page = "If you are buying the ".$item["name"].", then I will buy your ".$item2["name"]." for ".ceil($item2["buycost"] / 2)." gold. Is that ok?

"; - } else { - $page = "You are buying the ".$item["name"].", is that ok?

"; - } + // Retrieve current equipped item or create a default + $current_equip_id = user()->{$type_mapping[$item["type"]]['id']}; + if ($current_equip_id != 0) { + $item2 = get_item($current_equip_id); + } else { + $item2 = ["attribute" => 0, "buycost" => 0, "special" => "X"]; + } - display($page, "Buy Items"); -} + // Process special item effects + $specialFields = []; + $specialValues = []; + $powerAdjustments = 0; -/** - * Update user profile with new item & stats. - */ -function buy3($id) -{ + foreach ([$item, $item2] as $index => $process_item) { + if ($process_item["special"] != "X") { + $special = explode(",", $process_item["special"]); + $toChange = $special[0]; + $changeAmount = $index === 0 ? $special[1] : -$special[1]; - if (isset($_POST["cancel"])) redirect('/'); + user()[$toChange] += $changeAmount; + $specialFields[] = "$toChange = ?"; + $specialValues[] = user()[$toChange]; - $townrow = get_town_by_xy(user()->longitude, user()->latitude); - if ($townrow === false) display("Cheat attempt detected.

Get a life, loser.", "Error"); - $townitems = explode(",", $townrow["itemslist"]); - if (!in_array($id, $townitems)) display("Cheat attempt detected.

Get a life, loser.", "Error"); - - $item = get_item($id); - - if (user()->gold < $item["buycost"]) { - display("You do not have enough gold to buy this item.

You may return to town, store, or use the direction buttons on the left to start exploring.", "Buy Items"); - } - - $type_mapping = [ - 1 => ['id' => 'weaponid', 'name' => 'weaponname', 'power' => 'attackpower'], - 2 => ['id' => 'armorid', 'name' => 'armorname', 'power' => 'defensepower'], - 3 => ['id' => 'shieldid', 'name' => 'shieldname', 'power' => 'defensepower'] - ]; - - // Validate item type - if (!isset($type_mapping[$item["type"]])) { - display("Invalid item type.", "Error"); - } - - // Retrieve current equipped item or create a default - $current_equip_id = user()[$type_mapping[$item["type"]]['id']]; - if ($current_equip_id != 0) { - $item2 = get_item($current_equip_id); - } else { - $item2 = ["attribute" => 0, "buycost" => 0, "special" => "X"]; - } - - // Process special item effects - $specialFields = []; - $specialValues = []; - $powerAdjustments = 0; - - foreach ([$item, $item2] as $index => $process_item) { - if ($process_item["special"] != "X") { - $special = explode(",", $process_item["special"]); - $toChange = $special[0]; - $changeAmount = $index === 0 ? $special[1] : -$special[1]; - - user()[$toChange] += $changeAmount; - $specialFields[] = "$toChange = ?"; - $specialValues[] = user()[$toChange]; - - // Adjust attack or defense power - if ($toChange == "strength" || $toChange == "dexterity") { - $powerType = $toChange == "strength" ? "attackpower" : "defensepower"; - $powerAdjustments += $changeAmount; + // Adjust attack or defense power + if ($toChange == "strength" || $toChange == "dexterity") { + $powerType = $toChange == "strength" ? "attackpower" : "defensepower"; + $powerAdjustments += $changeAmount; + } } } + + // Determine power and type-specific updates + $currentType = $type_mapping[$item['type']]; + $powerField = $currentType['power']; + user()->$powerField += $item['attribute'] - $item2['attribute']; + + // Calculate new gold with trade-in value + user()->gold += ceil($item2['buycost'] / 2) - $item['buycost']; + + // Ensure current HP/MP/TP don't exceed max values + user()->currenthp = min(user()->currenthp, user()->maxhp); + user()->currentmp = min(user()->currentmp, user()->maxmp); + user()->currenttp = min(user()->currenttp, user()->maxtp); + + // Update item info in user + user()->{$type_mapping[$item['type']]['id']} = $item['id']; + user()->{$type_mapping[$item['type']]['name']} = $item['name']; + + user()->save(); + + $page = <<{$item['name']}.

+ You may return to town, shop, or use the direction buttons on the + left to start exploring. + HTML; + } else { + $type_to_row_mapping = [1 => 'weaponid', 2 => 'armorid', 3 => 'shieldid']; + $current_equipped_id = user()->{$type_to_row_mapping[$item['type']]} ?? 0; + + if ($current_equipped_id != 0) { + $item2 = get_item($current_equipped_id); + $sell_price = ceil($item2['buycost'] / 2); + $page = <<
+
+ + +
+ HTML; + } else { + $page = <<
+
+ + +
+ HTML; + } } - // Determine power and type-specific updates - $currentType = $type_mapping[$item["type"]]; - $powerField = $currentType['power']; - $newPower = user()[$powerField] + $item["attribute"] - $item2["attribute"]; - - // Calculate new gold with trade-in value - $newGold = user()->gold + ceil($item2["buycost"]/2) - $item["buycost"]; - - // Ensure current HP/MP/TP don't exceed max values - $newhp = min(user()->currenthp, user()->maxhp); - $newmp = min(user()->currentmp, user()->maxmp); - $newtp = min(user()->currenttp, user()->maxtp); - - $updateFields = array_merge( - $specialFields, - [ - "gold = ?", - "{$powerField} = ?", - "{$currentType['id']} = ?", - "{$currentType['name']} = ?", - "currenthp = ?", - "currentmp = ?", - "currenttp = ?" - ] - ); - - $updateValues = array_merge( - $specialValues, - [ - $newGold, - $newPower, - $item["id"], - $item["name"], - $newhp, - $newmp, - $newtp, - user()->id - ] - ); - - $stmt = db()->query("UPDATE users SET " . implode(", ", $updateFields) . " WHERE id = ?;", $updateValues); - if ($stmt === false) exit("Failed to purchase and equip $id. Go back and try again."); - - display("Thank you for purchasing this item.

You may return to town, store, or use the direction buttons on the left to start exploring.", "Buy Items"); + page_title('Buying '.$item['name']); + return is_htmx() ? $page : display($page, 'Buying '.$item['name']); } /** diff --git a/src/bootstrap.php b/src/bootstrap.php index 88ac6bb..b9ae927 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -19,7 +19,7 @@ require_once 'models/user.php'; env_load('../.env'); -$uri = uri(); +$uri = explode('/', trim($_SERVER['REQUEST_URI'], '/')); $GLOBALS['cache'] = []; $GLOBALS['state'] = []; diff --git a/src/lib.php b/src/lib.php index 7b0edf1..9700e92 100644 --- a/src/lib.php +++ b/src/lib.php @@ -20,8 +20,8 @@ function db(): Database function redirect(string $location): void { if (is_htmx()) { - header("HX-Redirect: $location"); - header("HX-Replace-Url: $location"); + $json = json_encode(['path' => $location, 'target' => '#'.$_SERVER['HTTP_HX_TARGET'] ?? '#middle']); + header("HX-Location: $json"); } else { header("Location: $location"); } @@ -452,14 +452,6 @@ 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'], '/')); -} - /** * Load the environment variables from the .env file. */ diff --git a/templates/babblebox.php b/templates/babblebox.php index 60cf9c9..8520891 100644 --- a/templates/babblebox.php +++ b/templates/babblebox.php @@ -10,7 +10,7 @@