Skip to content

Commit 6fd89f0

Browse files
Nyeriahclaude
andauthored
feat(resurrection-scroll): add user page and admin toggle (#181)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4b848cb commit 6fd89f0

6 files changed

Lines changed: 341 additions & 0 deletions

File tree

src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@
4242
</select>
4343
</td>
4444
</tr>
45+
<tr>
46+
<th>
47+
<label for="acore_resurrection_scroll">Scroll of Resurrection</label>
48+
</th>
49+
<td>
50+
<select name="acore_resurrection_scroll" id="acore_resurrection_scroll">
51+
<option value="0">Disabled</option>
52+
<option value="1" <?php if (Opts::I()->acore_resurrection_scroll == '1') echo 'selected'; ?>>Enabled</option>
53+
</select>
54+
</td>
55+
</tr>
56+
<tr class="acore_resurrection_scroll_config" <?php if (Opts::I()->acore_resurrection_scroll != '1') echo 'style="display:none;"'?>>
57+
<th>
58+
<label for="acore_resurrection_scroll_days_inactive">Days Inactive</label>
59+
</th>
60+
<td>
61+
<input type="number" name="acore_resurrection_scroll_days_inactive" id="acore_resurrection_scroll_days_inactive" min="1" value="<?= esc_attr(Opts::I()->acore_resurrection_scroll_days_inactive) ?>">
62+
</td>
63+
</tr>
4564
</tbody>
4665
</table>
4766
</div>
@@ -126,6 +145,10 @@
126145

127146
jQuery("#acore-name-unlock-thresholds-add").on("click", () => addThreshold());
128147

148+
jQuery('#acore_resurrection_scroll').on('change', function() {
149+
jQuery('.acore_resurrection_scroll_config').toggle();
150+
});
151+
129152
<?php foreach (Opts::I()->acore_name_unlock_thresholds as $i => $threshold) {
130153
if ($threshold[0] != "" && $threshold[1] != "") {
131154
echo "addThreshold($i, $threshold[0], $threshold[1]);";
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace ACore\Components\ResurrectionScrollMenu;
4+
5+
use ACore\Manager\ACoreServices;
6+
use ACore\Manager\Opts;
7+
use ACore\Components\ResurrectionScrollMenu\ResurrectionScrollView;
8+
9+
class ResurrectionScrollController
10+
{
11+
private $view;
12+
13+
public function __construct()
14+
{
15+
$this->view = new ResurrectionScrollView($this);
16+
}
17+
18+
public function render()
19+
{
20+
$data = $this->getScrollData();
21+
echo $this->view->getRender($data);
22+
}
23+
24+
private function getScrollData()
25+
{
26+
$accId = ACoreServices::I()->getAcoreAccountId();
27+
28+
if (!isset($accId) || !is_numeric($accId)) {
29+
return null;
30+
}
31+
32+
$conn = ACoreServices::I()->getCharacterEm()->getConnection();
33+
34+
// Get scroll account data
35+
$scrollData = null;
36+
try {
37+
$query = "SELECT `AccountId`, `EndDate`, `Expired`
38+
FROM `mod_ress_scroll_accounts`
39+
WHERE `AccountId` = ?";
40+
$stmt = $conn->prepare($query);
41+
$stmt->bindValue(1, $accId);
42+
$res = $stmt->executeQuery();
43+
$scrollData = $res->fetchAssociative();
44+
} catch (\Exception $e) {
45+
// Table may not exist if module is not installed
46+
}
47+
48+
// Get most recent character logout time
49+
$lastLogout = null;
50+
try {
51+
$query = "SELECT MAX(`logout_time`) as `last_logout`
52+
FROM `characters`
53+
WHERE `account` = ? AND `deleteDate` IS NULL";
54+
$stmt = $conn->prepare($query);
55+
$stmt->bindValue(1, $accId);
56+
$res = $stmt->executeQuery();
57+
$row = $res->fetchAssociative();
58+
if ($row && $row['last_logout']) {
59+
$lastLogout = (int) $row['last_logout'];
60+
}
61+
} catch (\Exception $e) {
62+
// Ignore
63+
}
64+
65+
$daysInactive = (int) Opts::I()->acore_resurrection_scroll_days_inactive;
66+
$isEligible = false;
67+
if ($lastLogout) {
68+
$daysSinceLogout = (int) ((time() - $lastLogout) / 86400);
69+
$isEligible = $daysSinceLogout >= $daysInactive;
70+
}
71+
72+
return [
73+
'accountId' => $accId,
74+
'scrollData' => $scrollData,
75+
'lastLogout' => $lastLogout,
76+
'daysInactive' => $daysInactive,
77+
'isEligible' => $isEligible,
78+
];
79+
}
80+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace ACore\Components\ResurrectionScrollMenu;
4+
5+
use ACore\Manager\Opts;
6+
use ACore\Components\ResurrectionScrollMenu\ResurrectionScrollController;
7+
8+
add_action('init', __NAMESPACE__ . '\\resurrection_scroll_menu_init');
9+
10+
class ResurrectionScrollMenu
11+
{
12+
private static $instance = null;
13+
14+
/**
15+
* Singleton
16+
* @return ResurrectionScrollMenu
17+
*/
18+
public static function I()
19+
{
20+
if (!self::$instance) {
21+
self::$instance = new self();
22+
}
23+
24+
return self::$instance;
25+
}
26+
27+
function acore_resurrection_scroll_menu()
28+
{
29+
if (Opts::I()->acore_resurrection_scroll == '1') {
30+
add_submenu_page('profile.php', 'Scroll of Resurrection', 'Scroll of Resurrection', 'read', ACORE_SLUG . '-resurrection-scroll', array($this, 'acore_resurrection_scroll_menu_page'));
31+
}
32+
}
33+
34+
function acore_resurrection_scroll_menu_page()
35+
{
36+
$controller = new ResurrectionScrollController();
37+
$controller->render();
38+
}
39+
}
40+
41+
function resurrection_scroll_menu_init()
42+
{
43+
$menu = ResurrectionScrollMenu::I();
44+
45+
add_action('admin_menu', array($menu, 'acore_resurrection_scroll_menu'));
46+
}
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
3+
namespace ACore\Components\ResurrectionScrollMenu;
4+
5+
class ResurrectionScrollView
6+
{
7+
private $controller;
8+
9+
public function __construct($controller)
10+
{
11+
$this->controller = $controller;
12+
}
13+
14+
public function getRender($data)
15+
{
16+
ob_start();
17+
18+
wp_enqueue_style('bootstrap-css', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css', array(), '5.1.3');
19+
wp_enqueue_style('acore-css', ACORE_URL_PLG . 'web/assets/css/main.css', array(), '0.1');
20+
wp_enqueue_script('bootstrap-js', '//cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', array(), '5.1.3');
21+
22+
// Determine account status
23+
$hasData = $data && isset($data['scrollData']) && $data['scrollData'];
24+
$isExpired = $hasData && $data['scrollData']['Expired'] == 1;
25+
$endDate = $hasData ? (int) $data['scrollData']['EndDate'] : null;
26+
$now = time();
27+
$isActive = $hasData && !$isExpired && $endDate > $now;
28+
$lastLogout = $data && isset($data['lastLogout']) ? $data['lastLogout'] : null;
29+
$daysInactive = $data ? (int) $data['daysInactive'] : 0;
30+
$isEligible = $data ? $data['isEligible'] : false;
31+
32+
if ($isActive) {
33+
$statusLabel = 'Active';
34+
$statusClass = 'success';
35+
} elseif ($isExpired || ($hasData && $endDate <= $now)) {
36+
$statusLabel = 'Expired';
37+
$statusClass = 'secondary';
38+
} else {
39+
$statusLabel = 'Not Enrolled';
40+
$statusClass = 'warning';
41+
}
42+
43+
?>
44+
45+
<div class="wrap">
46+
<h2>Scroll of Resurrection</h2>
47+
48+
<div class="row">
49+
<div class="col-sm-6">
50+
<div class="card">
51+
<div class="card-body">
52+
<h5>Your Account Status</h5>
53+
<hr>
54+
<?php if (!$data) { ?>
55+
<div class="alert alert-danger mb-0">Unable to retrieve account information.</div>
56+
<?php } else { ?>
57+
<table class="table table-bordered table-sm mb-0">
58+
<tbody>
59+
<tr>
60+
<th style="width: 40%;">Scroll Status</th>
61+
<td><span class="badge bg-<?= $statusClass ?>"><?= $statusLabel ?></span></td>
62+
</tr>
63+
<tr>
64+
<th>Eligibility</th>
65+
<td>
66+
<?php if ($isActive) { ?>
67+
<span class="badge bg-success">Enrolled</span>
68+
<?php } elseif ($isEligible) { ?>
69+
<span class="badge bg-info">Eligible</span>
70+
<span class="text-muted">- Log in to the game server to activate</span>
71+
<?php } else { ?>
72+
<span class="badge bg-secondary">Not Eligible</span>
73+
<?php if ($lastLogout) {
74+
$eligibleTimestamp = $lastLogout + ($daysInactive * 86400);
75+
$eligibleDate = new \DateTime();
76+
$eligibleDate->setTimestamp($eligibleTimestamp);
77+
$daysRemaining = (int) ceil(($eligibleTimestamp - time()) / 86400);
78+
if ($daysRemaining > 0) {
79+
echo '<span class="text-muted">- Eligible on <strong>' . esc_html($eligibleDate->format('M j, Y - H:i')) . '</strong> (' . $daysRemaining . ' days remaining)</span>';
80+
}
81+
} ?>
82+
<?php } ?>
83+
</td>
84+
</tr>
85+
<tr>
86+
<th>Last Character Logout</th>
87+
<td>
88+
<?php if ($lastLogout) {
89+
$logoutDate = new \DateTime();
90+
$logoutDate->setTimestamp($lastLogout);
91+
$daysSince = (int) ((time() - $lastLogout) / 86400);
92+
echo esc_html($logoutDate->format('M j, Y - H:i')) . ' <span class="text-muted">(' . $daysSince . ' days ago)</span>';
93+
} else {
94+
echo '<span class="text-muted">No characters found</span>';
95+
} ?>
96+
</td>
97+
</tr>
98+
<?php if ($hasData) { ?>
99+
<tr>
100+
<th>Bonus Expires</th>
101+
<td>
102+
<?php
103+
$expireDate = new \DateTime();
104+
$expireDate->setTimestamp($endDate);
105+
echo esc_html($expireDate->format('M j, Y - H:i'));
106+
if ($isActive) {
107+
$daysLeft = (int) ceil(($endDate - $now) / 86400);
108+
echo ' <span class="text-muted">(' . $daysLeft . ' days remaining)</span>';
109+
}
110+
?>
111+
</td>
112+
</tr>
113+
<?php } ?>
114+
</tbody>
115+
</table>
116+
<?php } ?>
117+
</div>
118+
</div>
119+
</div>
120+
121+
<div class="col-sm-6">
122+
<div class="card">
123+
<div class="card-body">
124+
<h5>Overview</h5>
125+
<hr>
126+
<p>
127+
The <strong>Scroll of Resurrection</strong> is a comeback incentive for inactive players.
128+
If your account has been inactive for an extended period, you will automatically receive
129+
<strong>rested XP bonuses</strong> when you log in, helping you catch up faster.
130+
</p>
131+
<p>
132+
The bonus grants a <strong>full rested XP pool</strong> for your current level each time
133+
you log in or level up, and lasts for a limited duration from your first eligible login.
134+
</p>
135+
</div>
136+
</div>
137+
</div>
138+
</div>
139+
140+
<div class="row mt-3">
141+
<div class="col-sm-6">
142+
<div class="card">
143+
<div class="card-body">
144+
<h5>How It Works</h5>
145+
<hr>
146+
<ol>
147+
<li>When you log in, the system checks how long your account has been inactive.</li>
148+
<li>If you have been away for long enough, your account becomes eligible for the bonus.</li>
149+
<li>You receive a <strong>full rested XP pool</strong> on every login and level-up.</li>
150+
<li>The bonus lasts for a set number of days from your first eligible login.</li>
151+
<li>Once the duration expires, the bonus stops automatically.</li>
152+
</ol>
153+
</div>
154+
</div>
155+
</div>
156+
157+
<div class="col-sm-6">
158+
<div class="card">
159+
<div class="card-body">
160+
<h5>Player Commands</h5>
161+
<hr>
162+
<table class="table table-bordered table-sm">
163+
<thead class="table-light">
164+
<tr>
165+
<th>Command</th>
166+
<th>Description</th>
167+
</tr>
168+
</thead>
169+
<tbody>
170+
<tr>
171+
<td><code>.rscroll info</code></td>
172+
<td>View your scroll eligibility status, last logout date, and bonus expiration</td>
173+
</tr>
174+
<tr>
175+
<td><code>.rscroll disable</code></td>
176+
<td>Toggle the rested XP bonus on or off for your character (already earned XP is kept)</td>
177+
</tr>
178+
</tbody>
179+
</table>
180+
</div>
181+
</div>
182+
</div>
183+
</div>
184+
</div>
185+
186+
<?php
187+
return ob_get_clean();
188+
}
189+
}

src/acore-wp-plugin/src/Manager/Opts.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class Opts {
3737
public $acore_db_eluna_name="";
3838
public $eluna_recruit_a_friend="";
3939
public $eluna_raf_config=["check_ip" => '0'];
40+
public $acore_resurrection_scroll="";
41+
public $acore_resurrection_scroll_days_inactive="180";
4042
public $acore_item_restoration="";
4143
public $acore_name_unlock_thresholds = [
4244
[5, 30], // level < 5 -> 30 days

src/acore-wp-plugin/src/boot.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require_once ACORE_PATH_PLG . 'src/Components/AdminPanel/AdminPanel.php';
1010
require_once ACORE_PATH_PLG . 'src/Components/CharactersMenu/CharactersMenu.php';
1111
require_once ACORE_PATH_PLG . 'src/Components/UnstuckMenu/UnstuckMenu.php';
12+
require_once ACORE_PATH_PLG . 'src/Components/ResurrectionScrollMenu/ResurrectionScrollMenu.php';
1213
require_once ACORE_PATH_PLG . 'src/Components/ServerInfo/ServerInfo.php';
1314
require_once ACORE_PATH_PLG . 'src/Components/Tools/ToolsInfo.php';
1415
require_once ACORE_PATH_PLG . 'src/Components/UserPanel/UserMenu.php';

0 commit comments

Comments
 (0)