Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/acore-wp-plugin/src/Components/AdminPanel/Pages/Tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@
</select>
</td>
</tr>
<tr>
<th>
<label for="acore_resurrection_scroll">Scroll of Resurrection</label>
</th>
<td>
<select name="acore_resurrection_scroll" id="acore_resurrection_scroll">
<option value="0">Disabled</option>
<option value="1" <?php if (Opts::I()->acore_resurrection_scroll == '1') echo 'selected'; ?>>Enabled</option>
</select>
</td>
</tr>
<tr class="acore_resurrection_scroll_config" <?php if (Opts::I()->acore_resurrection_scroll != '1') echo 'style="display:none;"'?>>
<th>
<label for="acore_resurrection_scroll_days_inactive">Days Inactive</label>
</th>
<td>
<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) ?>">
</td>
</tr>
</tbody>
</table>
</div>
Expand Down Expand Up @@ -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();
});

<?php foreach (Opts::I()->acore_name_unlock_thresholds as $i => $threshold) {
if ($threshold[0] != "" && $threshold[1] != "") {
echo "addThreshold($i, $threshold[0], $threshold[1]);";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace ACore\Components\ResurrectionScrollMenu;

use ACore\Manager\ACoreServices;
use ACore\Manager\Opts;
use ACore\Components\ResurrectionScrollMenu\ResurrectionScrollView;

class ResurrectionScrollController
{
private $view;

public function __construct()
{
$this->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,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace ACore\Components\ResurrectionScrollMenu;

use ACore\Manager\Opts;
use ACore\Components\ResurrectionScrollMenu\ResurrectionScrollController;

add_action('init', __NAMESPACE__ . '\\resurrection_scroll_menu_init');

class ResurrectionScrollMenu
{
private static $instance = null;

/**
* Singleton
* @return ResurrectionScrollMenu
*/
public static function I()
{
if (!self::$instance) {
self::$instance = new self();
}

return self::$instance;
}

function acore_resurrection_scroll_menu()
{
if (Opts::I()->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'));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

namespace ACore\Components\ResurrectionScrollMenu;

class ResurrectionScrollView
{
private $controller;

public function __construct($controller)
{
$this->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';
}

?>

<div class="wrap">
<h2>Scroll of Resurrection</h2>

<div class="row">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5>Your Account Status</h5>
<hr>
<?php if (!$data) { ?>
<div class="alert alert-danger mb-0">Unable to retrieve account information.</div>
<?php } else { ?>
<table class="table table-bordered table-sm mb-0">
<tbody>
<tr>
<th style="width: 40%;">Scroll Status</th>
<td><span class="badge bg-<?= $statusClass ?>"><?= $statusLabel ?></span></td>
</tr>
<tr>
<th>Eligibility</th>
<td>
<?php if ($isActive) { ?>
<span class="badge bg-success">Enrolled</span>
<?php } elseif ($isEligible) { ?>
<span class="badge bg-info">Eligible</span>
<span class="text-muted">- Log in to the game server to activate</span>
<?php } else { ?>
<span class="badge bg-secondary">Not Eligible</span>
<?php if ($lastLogout) {
$eligibleTimestamp = $lastLogout + ($daysInactive * 86400);
$eligibleDate = new \DateTime();
$eligibleDate->setTimestamp($eligibleTimestamp);
$daysRemaining = (int) ceil(($eligibleTimestamp - time()) / 86400);
if ($daysRemaining > 0) {
echo '<span class="text-muted">- Eligible on <strong>' . esc_html($eligibleDate->format('M j, Y - H:i')) . '</strong> (' . $daysRemaining . ' days remaining)</span>';
}
} ?>
<?php } ?>
</td>
</tr>
<tr>
<th>Last Character Logout</th>
<td>
<?php if ($lastLogout) {
$logoutDate = new \DateTime();
$logoutDate->setTimestamp($lastLogout);
$daysSince = (int) ((time() - $lastLogout) / 86400);
echo esc_html($logoutDate->format('M j, Y - H:i')) . ' <span class="text-muted">(' . $daysSince . ' days ago)</span>';
} else {
echo '<span class="text-muted">No characters found</span>';
} ?>
</td>
</tr>
<?php if ($hasData) { ?>
<tr>
<th>Bonus Expires</th>
<td>
<?php
$expireDate = new \DateTime();
$expireDate->setTimestamp($endDate);
echo esc_html($expireDate->format('M j, Y - H:i'));
if ($isActive) {
$daysLeft = (int) ceil(($endDate - $now) / 86400);
echo ' <span class="text-muted">(' . $daysLeft . ' days remaining)</span>';
}
?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } ?>
</div>
</div>
</div>

<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5>Overview</h5>
<hr>
<p>
The <strong>Scroll of Resurrection</strong> is a comeback incentive for inactive players.
If your account has been inactive for an extended period, you will automatically receive
<strong>rested XP bonuses</strong> when you log in, helping you catch up faster.
</p>
<p>
The bonus grants a <strong>full rested XP pool</strong> for your current level each time
you log in or level up, and lasts for a limited duration from your first eligible login.
</p>
</div>
</div>
</div>
</div>

<div class="row mt-3">
<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5>How It Works</h5>
<hr>
<ol>
<li>When you log in, the system checks how long your account has been inactive.</li>
<li>If you have been away for long enough, your account becomes eligible for the bonus.</li>
<li>You receive a <strong>full rested XP pool</strong> on every login and level-up.</li>
<li>The bonus lasts for a set number of days from your first eligible login.</li>
<li>Once the duration expires, the bonus stops automatically.</li>
</ol>
</div>
</div>
</div>

<div class="col-sm-6">
<div class="card">
<div class="card-body">
<h5>Player Commands</h5>
<hr>
<table class="table table-bordered table-sm">
<thead class="table-light">
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.rscroll info</code></td>
<td>View your scroll eligibility status, last logout date, and bonus expiration</td>
</tr>
<tr>
<td><code>.rscroll disable</code></td>
<td>Toggle the rested XP bonus on or off for your character (already earned XP is kept)</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

<?php
return ob_get_clean();
}
}
2 changes: 2 additions & 0 deletions src/acore-wp-plugin/src/Manager/Opts.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Opts {
public $acore_db_eluna_name="";
public $eluna_recruit_a_friend="";
public $eluna_raf_config=["check_ip" => '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
Expand Down
1 change: 1 addition & 0 deletions src/acore-wp-plugin/src/boot.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Loading