From 2263134e55743ec1c5dbb45f7f92e72f1c602434 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Fri, 4 Oct 2024 18:43:22 -0500 Subject: [PATCH] continue work on styling and form helpers --- database/auth.db | Bin 45056 -> 45056 bytes database/fights.db | Bin 45056 -> 45056 bytes database/live.db | Bin 188416 -> 188416 bytes database/scripts/create.php | 21 ++-- public/assets/css/dragon.css | 80 +++++++++++- public/assets/css/forms.css | 121 +++++++++++++++++++ public/assets/img/{deco-bar2.jpg => bar.jpg} | Bin public/index.php | 7 +- src/components.php | 24 ++++ src/controllers/auth.php | 29 +---- src/controllers/char.php | 108 ++++++++++++++++- src/helpers.php | 12 +- src/models/char.php | 103 +++++++++++++--- src/models/user.php | 5 +- templates/components/char_select_box.php | 7 ++ templates/pages/chars/delete.php | 24 ++++ templates/pages/chars/list.php | 45 +++++++ templates/pages/chars/select.php | 15 --- 18 files changed, 519 insertions(+), 82 deletions(-) rename public/assets/img/{deco-bar2.jpg => bar.jpg} (100%) create mode 100644 templates/components/char_select_box.php create mode 100644 templates/pages/chars/delete.php create mode 100644 templates/pages/chars/list.php delete mode 100644 templates/pages/chars/select.php diff --git a/database/auth.db b/database/auth.db index 5c2b22dde10a43378ffea81b1789673d7dc7ae2c..6c95f5e7888256bfa6510526705eadd280f9e1a8 100644 GIT binary patch delta 232 zcmZp8z|`=7X+oCRA_hJc{HtUwlKBLgE7T|)z10}};9 R3o8>-D+3EGLW>k8000}RLWKYT delta 193 zcmZp8z|`=7X+oCRX$C%D{=^>&5O93nK*+O*~P`h z8C#hr>+!vt?8<+TC7gjje6yf}FF$vq8Y35juwkP=2j}Djd9#dya2J=#GH-WtV{L7> zyx=OMh_E!Xa+RWdS3mu7lS<31$YjSbOHYe(?eqvY)8ufk-0}c}^zgFuKzD;sXH(0F eVj}}16J0|CT>}#ZLo+K=6DvalEJBMECIA4pAT~_^ diff --git a/database/fights.db b/database/fights.db index 70b3acf4bdf90550abc58ae3be538217c64c0d01..5bd7e4ffe6be5ea9589673928f08b03513bd705e 100644 GIT binary patch delta 48 zcmZp8z|`=7NhUbZC$l6~AuYcsH?c&)m_dMniHX5ML4kpRfor0SGbQ-cfQ$Dr`?hr6D`A1dPpgxwh$sJC08gYw$)u|Q+GwTG#|o| zC@6Xmp?wH?XswqbTzyywBIzY6sC)@TB(fin0)N0-lA=4ai$7%p!<;+cIro0w;oQD4 zxo=D!{*AT*Wkj{>bdKQMs7w8VE(N$^XgL?`47S9>on2HK1(nksHcSzmz!W3hLwgzZ z5YavOK31q44|m67(NKQABuS;*;AE^!#&5)i(*zfBn$h3k4w2KKA=Vx4RB9z2v2!K6|XP$#}(^-OR300m0;H(x*M5$@gsQv9ytP(Rs;p&Mpif@#lA~~Olx12Y{3jQu%OCQrC z+(WM3RsrKsM_!O)m>KXDQOxj7DPs~CfRdEqg+ww2XA;jF;f@&16SV4bFvLW=A3VHi zd?0bn4^?|D7>QmCN8_>1OZxWgI#g~DSwkBBmrS1BjuZPEkrs<>M?FBI~V{0&;eO>nzxPZ;5;GNyDmUpr4a8%FYLE%T{e}gTq@-sdjCr7cNAip>R(m z)Dtizft+DkNnab3X%;kVD{c~EYfKsSwwit&1ekJjnS zzZ1{|#fgb1yc~k6`zq+G-UA~#Rg+ZJ)3n3(Yrl;1EM_wVXE4KJHiad2jyHpB22EgJ jJGGYo=5fp4d6aOGZ8b}9)=KP0bn(>Tur3V`7Nmaw`Jn&Y delta 753 zcmZ{iPiPZC6vpT6&itxm_|f|)>6ca#57rOn>I<)sMU*I z5kWz;6x^FitDY20Q_MB?Qt)8gi&874AT%c_JvCS&?uu^Hg98JP@4cCi@5h^}NmDiH z-7m_t%kv7a;T=SGDt@I-6&cvWBz-MaOr=YDal%sph12jFqHA`4nKEIRShO zM0aiV2PGn_l7ak9y*R&~moqFZ+78^s;aL1tE;A7Vt&?nNqirJ1in1X$&)%q}5c zg)aM2K99Uep30BN32v6_#UHWGF0mubC5D3sprTi_Vf!6Cg*HL$4TC9Jku0J2ppe|wPD)8&9eCx>;lHi<``IS;_&Q9+RLla5Zp0x8GX{oOeT%o4Lw)N7jGq< z%+3$RK8VCzSv&eyKQ=B2YA6I|cPTYIs=EVj;f)U_VnYdSD0b#TLaR3VfJjmA0`fCF zqI15--n3Mgu1hT0APJ8gayL?^Fzq59z} zY={u7zA!)n^8eU9mfr?J3V{|boz`F$dd?50a#=l-bQn$S2fFPS)UZu3`X^B9OluuD z5X-7(;aq3G!mB5Pkaa0K4_ts3N1-d=DEn4+ruDM<1;TgzR8WIKxW~Ds&ZKhC#Gi+4 zt3M7`UP9!x0@}a(UIB>M3(0D8SkbDuz%)-+VC5&}I3kMT?55)J*wBFPA-ZR;-!>Hx I!dMi^Z$~)U-~a#s diff --git a/database/scripts/create.php b/database/scripts/create.php index b379939..3c4b5cb 100644 --- a/database/scripts/create.php +++ b/database/scripts/create.php @@ -63,6 +63,7 @@ if ($database === AUTH || $database === 'reset') { password TEXT NOT NULL, auth INT NOT NULL DEFAULT 0, char_id INTEGER NOT NULL DEFAULT 0, + char_slots INTEGER NOT NULL DEFAULT 3, created DATETIME DEFAULT CURRENT_TIMESTAMP, last_login DATETIME DEFAULT CURRENT_TIMESTAMP )'); @@ -406,14 +407,14 @@ if ($database === LIVE || $database === 'reset') { // Player inventory $db->exec('DROP TABLE IF EXISTS char_inventory'); - $r = $db->exec('CREATE TABLE inventory ( + $r = $db->exec('CREATE TABLE char_inventory ( char_id INTEGER NOT NULL, item_id INTEGER NOT NULL )'); // Create an index for char_id - $db->exec('CREATE INDEX idx_inventory_char_id ON inventory (char_id)'); + $db->exec('CREATE INDEX idx_inventory_char_id ON char_inventory (char_id)'); - created_or_error($r, 'inventory'); + created_or_error($r, 'char_inventory'); // Player wallet $db->exec('DROP TABLE IF EXISTS wallets'); @@ -429,7 +430,7 @@ if ($database === LIVE || $database === 'reset') { // Player bank $db->exec('DROP TABLE IF EXISTS char_bank'); - $r = $db->exec('CREATE TABLE bank ( + $r = $db->exec('CREATE TABLE char_bank ( char_id INTEGER NOT NULL, slots INTEGER NOT NULL DEFAULT 5, silver INTEGER NOT NULL DEFAULT 0, @@ -438,22 +439,22 @@ if ($database === LIVE || $database === 'reset') { last_collected DATETIME DEFAULT CURRENT_TIMESTAMP )'); // Create an index for char_id - $db->exec('CREATE INDEX idx_bank_char_id ON bank (char_id)'); + $db->exec('CREATE INDEX idx_bank_char_id ON char_bank (char_id)'); - created_or_error($r, 'bank'); + created_or_error($r, 'char_bank'); // Banked items $db->exec('DROP TABLE IF EXISTS char_banked_items'); - $r = $db->exec('CREATE TABLE banked_items ( + $r = $db->exec('CREATE TABLE char_banked_items ( char_id INTEGER NOT NULL, item_id INTEGER NOT NULL )'); // Create an index for char_id - $db->exec('CREATE INDEX idx_banked_items_char_id ON banked_items (char_id)'); + $db->exec('CREATE INDEX idx_banked_items_char_id ON char_banked_items (char_id)'); // Create an index for item_id - $db->exec('CREATE INDEX idx_banked_items_item_id ON banked_items (item_id)'); + $db->exec('CREATE INDEX idx_banked_items_item_id ON char_banked_items (item_id)'); - created_or_error($r, 'banked_items'); + created_or_error($r, 'char_banked_items'); // Towns $db->exec('DROP TABLE IF EXISTS towns'); diff --git a/public/assets/css/dragon.css b/public/assets/css/dragon.css index 8a5e3f7..bb070f5 100644 --- a/public/assets/css/dragon.css +++ b/public/assets/css/dragon.css @@ -74,6 +74,19 @@ body { border-color: #32373E #24282D #212429; } } + + &.danger { + background-color: #e57373; + background-image: linear-gradient(rgba(255, 255, 255, 0.15), rgba(139, 0, 0, 0.1)); + box-shadow: 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset; + border: 1px solid; + border-color: #d32f2f #c62828 #b71c1c; + + &:hover { + background-color: #d95c5c; + border-color: #b71c1c #a52727 #8e1f1f; + } + } } header { @@ -150,7 +163,7 @@ footer { height: 34px; color: white; gap: 1rem; - background-image: url('/assets/img/deco-bar2.jpg'); + background-image: url('/assets/img/bar.jpg'); & > div { display: flex; @@ -169,6 +182,11 @@ span.badge { color: #111111; border-radius: 0.25rem; padding: 0.1rem 0.25rem; + + &.dark { + background-color: #444c55; + color: white; + } } .my-1 { margin-bottom: 0.25rem; margin-top: 0.25rem; } @@ -265,3 +283,63 @@ span.badge { color: #666; font-family: monospace; } + +#center section { + &:not(:last-child) { + padding-bottom: 1rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + margin-bottom: 1rem; + } +} + +h1:has(.badge), h2:has(.badge), h3:has(.badge), h4:has(.badge), h5:has(.badge), h6:has(.badge) { + display: flex; + align-items: center; + + & > .badge { + margin-left: 0.5rem; + } +} + +.alert { + position: relative; + min-height: 1rem; + margin: 1rem 0; + background: #f8f8f9; + padding: 0.5rem 1rem; + line-height: 1.4285rem; + color: rgba(0, 0, 0, .87); + transition: opacity .1s ease, color .1s ease, background .1s ease, box-shadow .1s ease; + border-radius: .28571429rem; + box-shadow: 0 0 0 1px rgba(34, 36, 38, .22) inset, 0 0 0 0 transparent; + + &.success { + background-color: #f0f9eb; + color: #2c662d; + border-color: #b3dc9d; + } + + &.danger { + background-color: #f9e9eb; + color: #9f3a38; + border-color: #e0b4b4; + } + + &.warning { + background-color: #fff8e1; + color: #573a08; + border-color: #f9e79f; + } + + &.info { + background-color: #f0f9fb; + color: #2c7fba; + border-color: #b3d7f9; + } + + &.dark { + background-color: #f0f0f0; + color: #2c2c2c; + border-color: #b3b3b3; + } +} diff --git a/public/assets/css/forms.css b/public/assets/css/forms.css index c690cd0..65e6ad7 100644 --- a/public/assets/css/forms.css +++ b/public/assets/css/forms.css @@ -21,3 +21,124 @@ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; } + +/* +.radio-block { + & > input[type="radio"] { + display: none; + } + + & > label { + cursor: pointer; + display: inline-block; + border: none; + font-size: 1rem; + background: #f7f8fa linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1)); + box-shadow: 0 1px 0 1px rgba(255, 255, 255, 0.3) inset, 0 0 0 1px #adb2bb inset; + color: #111111; + padding: 0.5rem 1rem 0.5rem; + text-align: center; + border-radius: 3px; + user-select: none; + text-decoration: none; + transition: opacity 0.1s ease, background-color 0.1s ease, color 0.1s ease, background 0.1s ease; + -webkit-tap-highlight-color: transparent; + + &:hover { + background-color: #e0e0e0; + background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1)); + box-shadow: 0 1px 0 1px rgba(255, 255, 255, 0.3) inset, 0 0 0 1px #adb2bb inset; + color: rgba(0, 0, 0, 0.8); + } + + & > .badge { + background-color: #444c55; + color: white; + } + } + + &.active > label { + background-color: #444c55; + color: #ffffff; + background-image: linear-gradient(rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.1)); + border: 1px solid; + border-color: #3D444C #2F353B #2C3137; + box-shadow: 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset; + + & > .badge { + background-color: #f7f8fa; + color: #111111; + } + } + + /* When the radio button is checked, change the background color of the label + & > input[type="radio"]:checked + label { + background-color: #5a9bd4; + background-image: linear-gradient(rgba(255, 255, 255, 0.15), rgba(60, 100, 150, 0.1)); + box-shadow: 0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset; + border: 1px solid; + border-color: #4a8ab0 #3a7a9c #2a6a88; + } + + /* When the radio button is disabled, show a normal cursor + & > input[type="radio"]:disabled + label { + cursor: default; + } +} +*/ + +.radio-block-2 { + & > input[type="radio"] { + display: none; + } + + & > label { + cursor: pointer; + display: block; + + } +} + +.character-select > .radio-block { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 0.15rem; + + & > input[type="radio"] { + display: none; + } + + & > label { + display: flex; + align-items: center; + width: 100%; + border-radius: 0.15rem; + cursor: pointer; + transition: color, background-color 0.2s ease; + padding: 0.5rem; + + &:hover { + background-color: black; + color: white; + } + + & > .badge { + margin-left: 0.25rem; + } + } + + &.active > label { + background-color: black; + color: white; + } + + /* When the radio button is checked, change the background color of the label */ + & > input[type="radio"]:checked + label { + background-color: #f4cc67; + color: #111111; + } + + /* When the radio button is disabled, show a normal cursor */ + & > input[type="radio"]:disabled + label { + cursor: default; + } +} diff --git a/public/assets/img/deco-bar2.jpg b/public/assets/img/bar.jpg similarity index 100% rename from public/assets/img/deco-bar2.jpg rename to public/assets/img/bar.jpg diff --git a/public/index.php b/public/index.php index 380e258..9a2be78 100644 --- a/public/index.php +++ b/public/index.php @@ -28,10 +28,11 @@ router_post($r, '/auth/logout', 'auth_controller_logout_post'); /* Characters */ -router_get($r, '/characters', 'char_controller_select_get'); -router_post($r, '/character/create', 'char_controller_create_post'); -router_post($r, '/character/select', 'auth_controller_change_character_post'); +router_get($r, '/characters', 'char_controller_list_get'); +router_post($r, '/characters', 'char_controller_list_post'); router_get($r, '/character/create-first', 'char_controller_create_first_get'); +router_post($r, '/character/create', 'char_controller_create_post'); +router_post($r, '/character/delete', 'char_controller_delete_post'); /* Router diff --git a/src/components.php b/src/components.php index dd29d1f..f03cee1 100644 --- a/src/components.php +++ b/src/components.php @@ -38,3 +38,27 @@ function c_debug_query_log(): string { return render('components/debug_query_log'); } + +/** + * Render the character select radio buttons. + */ +function c_char_select_box(int $id, array $char): string +{ + return render('components/char_select_box', ['id' => $id, 'char' => $char]); +} + +/** + * Render an alert with a given type and message. + */ +function c_alert(string $t, string $m): string +{ + return "
$m
"; +} + +/** + * Generate a form field. + */ +function c_form_field(string $type, string $name, string $id, string $placeholder, bool $required = false): string +{ + return render('components/form_field', ['type' => $type, 'name' => $name, 'id' => $id, 'placeholder' => $placeholder, 'required' => $required]); +} diff --git a/src/controllers/auth.php b/src/controllers/auth.php index ff89057..e6eddc5 100644 --- a/src/controllers/auth.php +++ b/src/controllers/auth.php @@ -56,7 +56,7 @@ function auth_controller_register_post(): void // If there are errors at this point, send them to the page with errors flashed. if (!empty($errors)) { - flash('errors', $errors); + flash('alert-registration', ['errors', $errors]); redirect('/auth/register'); } @@ -76,7 +76,7 @@ function auth_controller_register_post(): void // If there are errors at this point, send them to the page with errors flashed. if (!empty($errors)) { - flash('errors', $errors); + flash('alert-registration', ['errors', $errors]); redirect('/auth/register'); } @@ -170,28 +170,3 @@ function auth_controller_logout_post(): void set_cookie('remember_me', '', 1); redirect('/'); } - -/** - * Changes the user's currently selected character. - */ -function auth_controller_change_character_post(): void -{ - auth_only(); - must_have_character(); - csrf_ensure(); - - $char_id = (int) ($_POST['char_id'] ?? 0); - - // If the character ID is the current character, do nothing. - if ($char_id === $_SESSION['user']['char_id']) redirect('/'); - - // Make sure the character ID is valid. - if (char_exists($char_id) === false) throw new Exception('Invalid character ID. (acccp)'); - - // Make sure the user owns the character. - if (char_belongs_to_user($char_id, $_SESSION['user']['id']) === false) router_error(999); - - change_user_character($char_id); - - redirect('/'); -} diff --git a/src/controllers/char.php b/src/controllers/char.php index 02e82f8..cf45db0 100644 --- a/src/controllers/char.php +++ b/src/controllers/char.php @@ -3,14 +3,110 @@ /** * Display a list of characters for the currently logged in user. */ -function char_controller_select_get(): void +function char_controller_list_get(): void { auth_only(); must_have_character(); $chars = char_list(user('id')); - echo render('layouts/basic', ['view' => 'pages/chars/select', 'chars' => $chars, 'activeTab' => nav_tabs['chars']]); + echo render('layouts/basic', ['view' => 'pages/chars/list', 'chars' => $chars, 'activeTab' => nav_tabs['chars']]); +} + +/** + * Handle an action from the character list page. + */ +function char_controller_list_post(): void +{ + auth_only(); + must_have_character(); + csrf_ensure(); + + $char_id = (int) ($_POST['char_id'] ?? 0); + $action = $_POST['action'] ?? ''; + + // If the character ID is not a number, or the action is not a string, return a 400. + if (!is_numeric($char_id) || !is_string($action)) router_error(400); + + // If the action is not one of the allowed actions, return a 400. + if (!in_array($action, ['select', 'delete'])) router_error(400); + + // If the action is to select a character, change the user's selected character. + if ($action === 'select') { + // If the character ID is the current character, do nothing. + if ($char_id === $_SESSION['user']['char_id'] || $char_id === 0) { + flash('info', 'You are already using that character.'); + redirect('/characters'); + } + + // Make sure the character ID is valid. + if (char_exists($char_id) === false) throw new Exception('Invalid character ID. (acccp)'); + + // Make sure the user owns the character. + if (!char_belongs_to_user($char_id, $_SESSION['user']['id'])) router_error(999); + + change_user_character($char_id); + + flash('success', 'Switched to character ' . char('name') . '!'); + } + + // If the action is to delete a character, move to the confirmation page. + if ($action === 'delete') { + // Make sure the character ID is valid. + if (char_exists($char_id) === false) throw new Exception('Invalid character ID. (accdp)'); + + // Make sure the user owns the character. + if (!char_belongs_to_user($char_id, $_SESSION['user']['id'])) router_error(999); + + $char = char_find($char_id); + + echo render('layouts/basic', ['view' => 'pages/chars/delete', 'char' => $char, 'activeTab' => nav_tabs['chars']]); + return; + } + + redirect('/characters'); +} + +/** + * Delete a character for the currently logged in user. + */ +function char_controller_delete_post(): void +{ + auth_only(); + must_have_character(); + csrf_ensure(); + + $char_id = (int) ($_POST['char_id'] ?? 0); + $name = $_POST['name'] ?? ''; + + // If the character ID is not a number, return a 400. + if (!is_numeric($char_id)) router_error(400); + + // Make sure the character ID is valid. + if (char_exists($char_id) === false) throw new Exception('Invalid character ID. (acddp)'); + + // Make sure the user owns the character. + if (!char_belongs_to_user($char_id, $_SESSION['user']['id'])) router_error(999); + + $char = char_find($char_id); + + // Confirm the name matches the name of the character. + if ($char['name'] !== $_POST['name']) { + flash('error', 'Failed to delete character. Name confirmation did not match.'); + redirect('/characters'); + } + + // Delete the character + char_delete($char_id); + + // If the character being deleted is the currently selected character, select the first character. + if ($_SESSION['user']['char_id'] === $char_id) { + $chars = char_list(user('id')); + if (count($chars) > 0) change_user_character($chars[0]['id']); + } + + flash('error', 'Character ' . $char['name'] . ' deleted.'); + redirect('/characters'); } /** @@ -57,8 +153,8 @@ function char_controller_create_post(): void // If there are errors at this point, send them to the page with errors flashed. if (!empty($errors)) { - flash('errors', $errors); - redirect('/'); + flash('alert-cl2', ['errors', $errors]); + redirect('/characters'); } // Create the character @@ -72,5 +168,7 @@ function char_controller_create_post(): void // Set the character as the user's selected character change_user_character($char); - redirect('/'); + flash('alert_character_list_1', ['success', 'Character ' . $name . ' created!']); + redirect('/characters'); } + diff --git a/src/helpers.php b/src/helpers.php index ebc4dd3..6582e16 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -37,7 +37,7 @@ function redirect(string $location): void } /** - * Flash a message to the session, or retrieve an existing flash value. + * Flash data to the session, or retrieve an existing flash value. Returns false if the key does not exist. */ function flash(string $key, mixed $value = ''): mixed { @@ -186,3 +186,13 @@ function wallet(string $field = ''): array|int|false if ($field === '') return $GLOBALS['wallet']; return $GLOBALS['wallet'][$field] ?? false; } + +/** + * Format an array of strings to a ul element. + */ +function array_to_ul(array $array): string +{ + $html = ''; + foreach ($array as $item) $html .= "
  • $item
  • "; + return "
      $html
    "; +} diff --git a/src/models/char.php b/src/models/char.php index a10da4c..d78efb4 100644 --- a/src/models/char.php +++ b/src/models/char.php @@ -72,23 +72,6 @@ function char_gear_create(int $char_id, array $initialGear = []): void } } -/** - * Create the character's bank account. The bank stores items and currency, with an interest rate based on - * the bank account's tier. The bank account has a limited number of slots, which can be increased by upgrading - * the bank account. The bank account starts with 0 silver and 5 slots. - */ -function char_bank_create(int $char_id, int $slots = 5, int $silver = 0, int $tier = 0): void -{ - if (db_query(db_live(), "INSERT INTO char_banks (char_id, slots, silver, tier) VALUES (:p, :s, :si, :t)", [ - ':p' => $char_id, - ':s' => $slots, - ':si' => $silver, - ':t' => $tier - ]) === false) { - throw new Exception('Failed to create character bank. (cbc)'); - } -} - /** * Get a charcter by their ID. Returns the character's data as an associative array. */ @@ -161,3 +144,89 @@ function char_belongs_to_user(int $char_id, int $user_id): bool if ($char === false) throw new Exception('Character not found. (cbtu)'); return $char['user_id'] === $user_id; } + +/** + * Delete a character by their ID. This will delete all associated data tables as well. + */ +function char_delete(int $char_id): void +{ + // Delete the character + if (db_query(db_live(), "DELETE FROM characters WHERE id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character. (cd)'); + } + + // Get item IDs from the character's inventory + $items = db_query(db_live(), "SELECT item_id FROM char_inventory WHERE char_id = :p", [':p' => $char_id]); + // delete the character's inventory and items + while ($row = $items->fetchArray(SQLITE3_ASSOC)) { + if (db_query(db_live(), "DELETE FROM char_inventory WHERE char_id = :c", [':c' => $char_id]) === false) { + throw new Exception('Failed to delete character inventory. (cd)'); + } + + if (db_query(db_live(), "DELETE FROM items WHERE id = :p", [':p' => $row['id']]) === false) { + throw new Exception('Failed to delete character item slots. (cd)'); + } + } + + // Delete the character's location + if (db_query(db_live(), "DELETE FROM char_locations WHERE char_id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character location. (cd)'); + } + + // Delete the character's gear + if (db_query(db_live(), "DELETE FROM char_gear WHERE char_id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character gear. (cd)'); + } + + // Delete the character's bank + if (db_query(db_live(), "DELETE FROM char_bank WHERE char_id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character bank. (cd)'); + } + + // Delete character's banked items + if (db_query(db_live(), "DELETE FROM char_banked_items WHERE char_id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character bank items. (cd)'); + } + + // Delete the user's guild membership + if (db_query(db_live(), "DELETE FROM guild_members WHERE char_id = :p", [':p' => $char_id]) === false) { + throw new Exception('Failed to delete character guild membership. (cd)'); + } + + // if the character was a guild leader, hand leadership to the next highest ranking member + $guild = db_query(db_live(), "SELECT id FROM guilds WHERE leader_id = :p", [':p' => $char_id])->fetchArray(SQLITE3_ASSOC); + if ($guild !== false) { + $members = db_query(db_live(), "SELECT char_id FROM guild_members WHERE guild_id = :p ORDER BY rank DESC", [':p' => $guild['id']]); + $newLeader = $members->fetchArray(SQLITE3_ASSOC); + if ($newLeader !== false) { + db_query(db_live(), "UPDATE guilds SET leader_id = :p WHERE id = :g", [':p' => $newLeader['char_id'], ':g' => $guild['id']]); + } + } + + // Get a list of all pve fight IDs. + $pve = db_query(db_fights(), "SELECT id FROM pve WHERE char_id = :p", [':p' => $char_id]); + // Get a list of all pvp fight IDs. + $pvp = db_query(db_fights(), "SELECT id FROM pvp WHERE char1_id = :p OR char2_id = :p", [':p' => $char_id]); + + // Delete all pve fights + while ($row = $pve->fetchArray(SQLITE3_ASSOC)) { + if (db_query(db_fights(), "DELETE FROM pve WHERE id = :p", [':p' => $row['id']]) === false) { + throw new Exception('Failed to delete pve fight. (cd)'); + } + + if (db_query(db_fights(), "DELETE FROM pve_logs WHERE fight_id = :p", [':p' => $row['id']]) === false) { + throw new Exception('Failed to delete pve fight logs. (cd)'); + } + } + + // Delete all pvp fights + while ($row = $pvp->fetchArray(SQLITE3_ASSOC)) { + if (db_query(db_fights(), "DELETE FROM pvp WHERE id = :p", [':p' => $row['id']]) === false) { + throw new Exception('Failed to delete pvp fight. (cd)'); + } + + if (db_query(db_fights(), "DELETE FROM pvp_logs WHERE fight_id = :p", [':p' => $row['id']]) === false) { + throw new Exception('Failed to delete pvp fight logs. (cd)'); + } + } +} diff --git a/src/models/user.php b/src/models/user.php index 80a7506..c2a475e 100644 --- a/src/models/user.php +++ b/src/models/user.php @@ -37,9 +37,8 @@ function user_delete(string|int $user): SQLite3Result|false } /** - * Creates a character's wallet. A character's wallet is where they store their currencies. Can optionally specify the - * starting balances of the wallet. Returns the created wallet's ID. If a currency is set to -1, the starting_silver - * or starting_star_gems fields from the env will be used. + * Creates an account wallet. Can optionally specify the starting balances of the wallet. Returns the created wallet's + * ID. If a currency is set to -1, the starting_silver or starting_star_gems fields from the env will be used. */ function wallet_create(int $user_id, int $silver = -1, int $starGems = -1): void { diff --git a/templates/components/char_select_box.php b/templates/components/char_select_box.php new file mode 100644 index 0000000..4781f90 --- /dev/null +++ b/templates/components/char_select_box.php @@ -0,0 +1,7 @@ +
    + > + +
    diff --git a/templates/pages/chars/delete.php b/templates/pages/chars/delete.php new file mode 100644 index 0000000..a119b1c --- /dev/null +++ b/templates/pages/chars/delete.php @@ -0,0 +1,24 @@ +
    +

    Delete Character

    + +

    + Warning! This action is irreversible. Once you delete a character, it is gone forever. All items, banked items and currency, + fight records, and other data associated with the character will be lost. +

    + +

    + Are you absolutely sure you want to delete this character? If so, type the character's name below to confirm. + Otherwise, click the back button to cancel. +

    + +
    + + + + + + + + Back +
    +
    diff --git a/templates/pages/chars/list.php b/templates/pages/chars/list.php new file mode 100644 index 0000000..6ae6990 --- /dev/null +++ b/templates/pages/chars/list.php @@ -0,0 +1,45 @@ +
    + + +

    Characters

    + 0): ?> +
    + + +
    + $char) echo c_char_select_box($id, $char); ?> +
    + + + +
    + + +

    You have no characters.

    + +
    + +
    + + + count($chars)): ?> +

    Create a new character

    + +

    You have left.

    +
    + + +
    + +
    + + +
    + +
    diff --git a/templates/pages/chars/select.php b/templates/pages/chars/select.php deleted file mode 100644 index d96d262..0000000 --- a/templates/pages/chars/select.php +++ /dev/null @@ -1,15 +0,0 @@ -

    Characters

    - 0): ?> -
    - - $char): ?> - -
    - - -
    - - -

    You have no characters.

    -