From 05ee6e12983844258b3ea4368225d3b43c2e9308 Mon Sep 17 00:00:00 2001 From: Skjalf <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:57:39 -0300 Subject: [PATCH 1/3] feat(resurrection-scroll): add Scroll of Resurrection user page and admin toggle Add a user-facing page under the Profile menu that displays the player's Scroll of Resurrection status, eligibility, and bonus expiration. Includes an admin toggle in Tools to enable/disable the page and configure the days of inactivity required for eligibility. Co-Authored-By: Claude Opus 4.6 --- .../src/Components/AdminPanel/Pages/Tools.php | 23 +++ .../ResurrectionScrollController.php | 80 ++++++++ .../ResurrectionScrollMenu.php | 46 +++++ .../ResurrectionScrollView.php | 189 ++++++++++++++++++ src/acore-wp-plugin/src/Manager/Opts.php | 2 + src/acore-wp-plugin/src/boot.php | 1 + 6 files changed, 341 insertions(+) create mode 100644 src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollController.php create mode 100644 src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php create mode 100644 src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php diff --git a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php index 0252f843..86058a20 100644 --- a/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php +++ b/src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php @@ -42,6 +42,25 @@ + + + + + + + + + acore_resurrection_scroll != '1') echo 'style="display:none;"'?>> + + + + + + + @@ -126,6 +145,10 @@ jQuery("#acore-name-unlock-thresholds-add").on("click", () => addThreshold()); + jQuery('#acore_resurrection_scroll').on('change', function() { + jQuery('.acore_resurrection_scroll_config').toggle(); + }); + acore_name_unlock_thresholds as $i => $threshold) { if ($threshold[0] != "" && $threshold[1] != "") { echo "addThreshold($i, $threshold[0], $threshold[1]);"; diff --git a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollController.php b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollController.php new file mode 100644 index 00000000..ff8ad2d6 --- /dev/null +++ b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollController.php @@ -0,0 +1,80 @@ +view = new ResurrectionScrollView($this); + } + + public function render() + { + $data = $this->getScrollData(); + echo $this->view->getRender($data); + } + + private function getScrollData() + { + $accId = ACoreServices::I()->getAcoreAccountId(); + + if (!isset($accId) || !is_numeric($accId)) { + return null; + } + + $conn = ACoreServices::I()->getCharacterEm()->getConnection(); + + // Get scroll account data + $scrollData = null; + try { + $query = "SELECT `AccountId`, `EndDate`, `Expired` + FROM `mod_ress_scroll_accounts` + WHERE `AccountId` = ?"; + $stmt = $conn->prepare($query); + $stmt->bindValue(1, $accId); + $res = $stmt->executeQuery(); + $scrollData = $res->fetchAssociative(); + } catch (\Exception $e) { + // Table may not exist if module is not installed + } + + // Get most recent character logout time + $lastLogout = null; + try { + $query = "SELECT MAX(`logout_time`) as `last_logout` + FROM `characters` + WHERE `account` = ? AND `deleteDate` IS NULL"; + $stmt = $conn->prepare($query); + $stmt->bindValue(1, $accId); + $res = $stmt->executeQuery(); + $row = $res->fetchAssociative(); + if ($row && $row['last_logout']) { + $lastLogout = (int) $row['last_logout']; + } + } catch (\Exception $e) { + // Ignore + } + + $daysInactive = (int) Opts::I()->acore_resurrection_scroll_days_inactive; + $isEligible = false; + if ($lastLogout) { + $daysSinceLogout = (int) ((time() - $lastLogout) / 86400); + $isEligible = $daysSinceLogout >= $daysInactive; + } + + return [ + 'accountId' => $accId, + 'scrollData' => $scrollData, + 'lastLogout' => $lastLogout, + 'daysInactive' => $daysInactive, + 'isEligible' => $isEligible, + ]; + } +} diff --git a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php new file mode 100644 index 00000000..4f11e8ee --- /dev/null +++ b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php @@ -0,0 +1,46 @@ +acore_resurrection_scroll == '1') { + add_submenu_page('profile.php', 'Scroll of Resurrection', 'Scroll of Resurrection', 'read', ACORE_SLUG . '-resurrection-scroll', array($this, 'acore_resurrection_scroll_menu_page')); + } + } + + function acore_resurrection_scroll_menu_page() + { + $controller = new ResurrectionScrollController(); + $controller->render(); + } +} + +function resurrection_scroll_menu_init() +{ + $menu = ResurrectionScrollMenu::I(); + + add_action('admin_menu', array($menu, 'acore_resurrection_scroll_menu')); +} diff --git a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php new file mode 100644 index 00000000..8d2298c4 --- /dev/null +++ b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php @@ -0,0 +1,189 @@ +controller = $controller; + } + + public function getRender($data) + { + ob_start(); + + wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3'); + wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1'); + wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3'); + + // Determine account status + $hasData = $data && isset($data['scrollData']) && $data['scrollData']; + $isExpired = $hasData && $data['scrollData']['Expired'] == 1; + $endDate = $hasData ? (int) $data['scrollData']['EndDate'] : null; + $now = time(); + $isActive = $hasData && !$isExpired && $endDate > $now; + $lastLogout = $data && isset($data['lastLogout']) ? $data['lastLogout'] : null; + $daysInactive = $data ? (int) $data['daysInactive'] : 0; + $isEligible = $data ? $data['isEligible'] : false; + + if ($isActive) { + $statusLabel = 'Active'; + $statusClass = 'success'; + } elseif ($isExpired || ($hasData && $endDate <= $now)) { + $statusLabel = 'Expired'; + $statusClass = 'secondary'; + } else { + $statusLabel = 'Not Enrolled'; + $statusClass = 'warning'; + } + +?> + +
+

Scroll of Resurrection

+ +
+
+
+
+
Your Account Status
+
+ +
Unable to retrieve account information.
+ + + + + + + + + + + + + + + + + + + + + + +
Scroll Status
Eligibility + + Enrolled + + Eligible + - Log in to the game server to activate + + Not Eligible + - Requires days of inactivity 0) { + echo ' (' . $daysRemaining . ' days remaining)'; + } + } + ?> + +
Last Character Logout + setTimestamp($lastLogout); + $daysSince = (int) ((time() - $lastLogout) / 86400); + echo esc_html($logoutDate->format('M j, Y - H:i')) . ' (' . $daysSince . ' days ago)'; + } else { + echo 'No characters found'; + } ?> +
Bonus Expires + setTimestamp($endDate); + echo esc_html($expireDate->format('M j, Y - H:i')); + if ($isActive) { + $daysLeft = (int) ceil(($endDate - $now) / 86400); + echo ' (' . $daysLeft . ' days remaining)'; + } + ?> +
+ +
+
+
+ +
+
+
+
Overview
+
+

+ The Scroll of Resurrection is a comeback incentive for inactive players. + If your account has been inactive for an extended period, you will automatically receive + rested XP bonuses when you log in, helping you catch up faster. +

+

+ The bonus grants a full rested XP pool for your current level each time + you log in or level up, and lasts for a limited duration from your first eligible login. +

+
+
+
+
+ +
+
+
+
+
How It Works
+
+
    +
  1. When you log in, the system checks how long your account has been inactive.
  2. +
  3. If you have been away for long enough, your account becomes eligible for the bonus.
  4. +
  5. You receive a full rested XP pool on every login and level-up.
  6. +
  7. The bonus lasts for a set number of days from your first eligible login.
  8. +
  9. Once the duration expires, the bonus stops automatically.
  10. +
+
+
+
+ +
+
+
+
Player Commands
+
+ + + + + + + + + + + + + + + + + +
CommandDescription
.rscroll infoView your scroll eligibility status, last logout date, and bonus expiration
.rscroll disableToggle the rested XP bonus on or off for your character (already earned XP is kept)
+
+
+
+
+
+ + '0']; + public $acore_resurrection_scroll=""; + public $acore_resurrection_scroll_days_inactive="180"; public $acore_item_restoration=""; public $acore_name_unlock_thresholds = [ [5, 30], // level < 5 -> 30 days diff --git a/src/acore-wp-plugin/src/boot.php b/src/acore-wp-plugin/src/boot.php index ea262eec..b404616d 100644 --- a/src/acore-wp-plugin/src/boot.php +++ b/src/acore-wp-plugin/src/boot.php @@ -9,6 +9,7 @@ require_once ACORE_PATH_PLG . 'src/Components/AdminPanel/AdminPanel.php'; require_once ACORE_PATH_PLG . 'src/Components/CharactersMenu/CharactersMenu.php'; require_once ACORE_PATH_PLG . 'src/Components/UnstuckMenu/UnstuckMenu.php'; +require_once ACORE_PATH_PLG . 'src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php'; require_once ACORE_PATH_PLG . 'src/Components/ServerInfo/ServerInfo.php'; require_once ACORE_PATH_PLG . 'src/Components/Tools/ToolsInfo.php'; require_once ACORE_PATH_PLG . 'src/Components/UserPanel/UserMenu.php'; From 07236bfb43d141bf543d14c6e005329bc91419bf Mon Sep 17 00:00:00 2001 From: Skjalf <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 25 Mar 2026 02:59:53 -0300 Subject: [PATCH 2/3] feat(resurrection-scroll): display explicit eligibility date Show the exact date the account becomes eligible (e.g. "Eligible on Jul 15, 2026") alongside the remaining days count. Co-Authored-By: Claude Opus 4.6 --- .../ResurrectionScrollView.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php index 8d2298c4..d867093a 100644 --- a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php +++ b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php @@ -70,15 +70,15 @@ public function getRender($data) - Log in to the game server to activate Not Eligible - - Requires days of inactivity 0) { - echo ' (' . $daysRemaining . ' days remaining)'; - } + setTimestamp($eligibleTimestamp); + $daysRemaining = (int) ceil(($eligibleTimestamp - time()) / 86400); + if ($daysRemaining > 0) { + echo '- Eligible on ' . esc_html($eligibleDate->format('M j, Y')) . ' (' . $daysRemaining . ' days remaining)'; } - ?> + } ?> From 400357444849aa2ec3b3d838c60f6d75aeccece9 Mon Sep 17 00:00:00 2001 From: Skjalf <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:01:13 -0300 Subject: [PATCH 3/3] fix(resurrection-scroll): include time in eligibility date The module compares exact unix timestamps, not just dates, so hours and minutes are relevant for eligibility calculation. Co-Authored-By: Claude Opus 4.6 --- .../ResurrectionScrollMenu/ResurrectionScrollView.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php index d867093a..345fa33b 100644 --- a/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php +++ b/src/acore-wp-plugin/src/Components/ResurrectionScrollMenu/ResurrectionScrollView.php @@ -76,7 +76,7 @@ public function getRender($data) $eligibleDate->setTimestamp($eligibleTimestamp); $daysRemaining = (int) ceil(($eligibleTimestamp - time()) / 86400); if ($daysRemaining > 0) { - echo '- Eligible on ' . esc_html($eligibleDate->format('M j, Y')) . ' (' . $daysRemaining . ' days remaining)'; + echo '- Eligible on ' . esc_html($eligibleDate->format('M j, Y - H:i')) . ' (' . $daysRemaining . ' days remaining)'; } } ?>