You've already forked Arcturus-Morningstar-Extended
mirror of
https://github.com/duckietm/Arcturus-Morningstar-Extended.git
synced 2026-06-19 15:06:19 +00:00
feat(earnings): gate rewards by user progress
This commit is contained in:
@@ -117,11 +117,16 @@ INSERT IGNORE INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
|||||||
INSERT IGNORE INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
INSERT IGNORE INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
||||||
('earnings.daily_gift.native.enabled', '0', 'Use native hotel subsystem data for daily gift earnings claims when available.'),
|
('earnings.daily_gift.native.enabled', '0', 'Use native hotel subsystem data for daily gift earnings claims when available.'),
|
||||||
('earnings.games.native.enabled', '0', 'Use native hotel subsystem data for games earnings claims when available.'),
|
('earnings.games.native.enabled', '0', 'Use native hotel subsystem data for games earnings claims when available.'),
|
||||||
('earnings.achievements.native.enabled', '0', 'Use native hotel subsystem data for achievements earnings claims when available.'),
|
('earnings.achievements.native.enabled', '1', 'Use achievement score thresholds for achievements earnings claims.'),
|
||||||
('earnings.marketplace.native.enabled', '1', 'Use marketplace sold item payouts for marketplace earnings claims.'),
|
('earnings.marketplace.native.enabled', '1', 'Use marketplace sold item payouts for marketplace earnings claims.'),
|
||||||
('earnings.hc_payday.native.enabled', '1', 'Use unclaimed HC payday logs for HC payday earnings claims.'),
|
('earnings.hc_payday.native.enabled', '1', 'Use unclaimed HC payday logs for HC payday earnings claims.'),
|
||||||
('earnings.level_progress.native.enabled', '0', 'Use native hotel subsystem data for level progress earnings claims when available.'),
|
('earnings.level_progress.native.enabled', '1', 'Use talent track levels for level progress earnings claims.'),
|
||||||
('earnings.donations.native.enabled', '0', 'Use native hotel subsystem data for donations earnings claims when available.'),
|
('earnings.donations.native.enabled', '0', 'Use native hotel subsystem data for donations earnings claims when available.'),
|
||||||
('earnings.bonus_bag.native.enabled', '0', 'Use native hotel subsystem data for bonus bag earnings claims when available.'),
|
('earnings.bonus_bag.native.enabled', '0', 'Use native hotel subsystem data for bonus bag earnings claims when available.'),
|
||||||
('earnings.mystery_boxes.native.enabled', '0', 'Use native hotel subsystem data for mystery boxes earnings claims when available.'),
|
('earnings.mystery_boxes.native.enabled', '0', 'Use native hotel subsystem data for mystery boxes earnings claims when available.'),
|
||||||
('earnings.club_job.native.enabled', '0', 'Use native hotel subsystem data for club and job earnings claims when available.');
|
('earnings.club_job.native.enabled', '0', 'Use native hotel subsystem data for club and job earnings claims when available.');
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `emulator_settings` (`key`, `value`, `comment`) VALUES
|
||||||
|
('earnings.achievements.min_score', '1', 'Minimum achievement score required before achievements earnings can be claimed.'),
|
||||||
|
('earnings.achievements.score.step', '100', 'Achievement score bucket size used to prevent repeated claims for the same progress band.'),
|
||||||
|
('earnings.level_progress.min_level', '1', 'Minimum citizenship/helper talent level required before level progress earnings can be claimed.');
|
||||||
|
|||||||
@@ -517,8 +517,12 @@ public final class Emulator {
|
|||||||
Emulator.config.register(prefix + "item_id", "0");
|
Emulator.config.register(prefix + "item_id", "0");
|
||||||
Emulator.config.register(prefix + "item.quantity", "1");
|
Emulator.config.register(prefix + "item.quantity", "1");
|
||||||
Emulator.config.register(prefix + "hc.days", "0");
|
Emulator.config.register(prefix + "hc.days", "0");
|
||||||
Emulator.config.register(prefix + "native.enabled", (category.equals("marketplace") || category.equals("hc_payday")) ? "1" : "0");
|
Emulator.config.register(prefix + "native.enabled", (category.equals("marketplace") || category.equals("hc_payday") || category.equals("achievements") || category.equals("level_progress")) ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Emulator.config.register("earnings.achievements.min_score", "1");
|
||||||
|
Emulator.config.register("earnings.achievements.score.step", "100");
|
||||||
|
Emulator.config.register("earnings.level_progress.min_level", "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RCONServer getRconServer() {
|
public static RCONServer getRconServer() {
|
||||||
|
|||||||
@@ -95,7 +95,11 @@ public class EarningsCenterManager {
|
|||||||
return new EarningsClaimResult(category, EarningsClaimResult.Status.NO_REWARD, buildEntry(habbo, userId, category, now));
|
return new EarningsClaimResult(category, EarningsClaimResult.Status.NO_REWARD, buildEntry(habbo, userId, category, now));
|
||||||
}
|
}
|
||||||
|
|
||||||
String periodKey = periodKey(now, definition.cooldownSeconds());
|
if (!isEligibleForProgressClaim(habbo, category)) {
|
||||||
|
return new EarningsClaimResult(category, EarningsClaimResult.Status.NO_REWARD, buildEntry(habbo, userId, category, now));
|
||||||
|
}
|
||||||
|
|
||||||
|
String periodKey = periodKey(habbo, category, now, definition.cooldownSeconds());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!this.claims.recordClaim(userId, category.getKey(), periodKey, now)) {
|
if (!this.claims.recordClaim(userId, category.getKey(), periodKey, now)) {
|
||||||
@@ -143,7 +147,11 @@ public class EarningsCenterManager {
|
|||||||
return new EarningsEntry(category, true, claimable, 0, definition.rewards());
|
return new EarningsEntry(category, true, claimable, 0, definition.rewards());
|
||||||
}
|
}
|
||||||
|
|
||||||
String periodKey = periodKey(now, definition.cooldownSeconds());
|
if (!isEligibleForProgressClaim(habbo, category)) {
|
||||||
|
return new EarningsEntry(category, true, false, 0, definition.rewards());
|
||||||
|
}
|
||||||
|
|
||||||
|
String periodKey = periodKey(habbo, category, now, definition.cooldownSeconds());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
claimable = !this.claims.hasClaim(userId, category.getKey(), periodKey);
|
claimable = !this.claims.hasClaim(userId, category.getKey(), periodKey);
|
||||||
@@ -186,6 +194,25 @@ public class EarningsCenterManager {
|
|||||||
return this.config.getBoolean(CONFIG_PREFIX + category.getKey() + ".native.enabled", true);
|
return this.config.getBoolean(CONFIG_PREFIX + category.getKey() + ".native.enabled", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isEligibleForProgressClaim(Habbo habbo, EarningsCategory category) {
|
||||||
|
if (!nativeEnabled(category) || habbo == null || habbo.getHabboStats() == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category == EarningsCategory.ACHIEVEMENTS) {
|
||||||
|
int minimumScore = Math.max(1, this.config.getInt(CONFIG_PREFIX + category.getKey() + ".min_score", 1));
|
||||||
|
return habbo.getHabboStats().getAchievementScore() >= minimumScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category == EarningsCategory.LEVEL_PROGRESS) {
|
||||||
|
int minimumLevel = Math.max(0, this.config.getInt(CONFIG_PREFIX + category.getKey() + ".min_level", 1));
|
||||||
|
int highestLevel = Math.max(habbo.getHabboStats().citizenshipLevel, habbo.getHabboStats().helpersLevel);
|
||||||
|
return highestLevel >= minimumLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void addReward(List<EarningsReward> rewards, String type, int amount, int pointsType) {
|
private void addReward(List<EarningsReward> rewards, String type, int amount, int pointsType) {
|
||||||
int clampedAmount = Math.min(Math.max(0, amount), MAX_CONFIGURED_REWARD);
|
int clampedAmount = Math.min(Math.max(0, amount), MAX_CONFIGURED_REWARD);
|
||||||
if (clampedAmount > 0) {
|
if (clampedAmount > 0) {
|
||||||
@@ -229,7 +256,19 @@ public class EarningsCenterManager {
|
|||||||
return (int) (this.clock.instant().getEpochSecond());
|
return (int) (this.clock.instant().getEpochSecond());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String periodKey(int now, int cooldownSeconds) {
|
private String periodKey(Habbo habbo, EarningsCategory category, int now, int cooldownSeconds) {
|
||||||
|
if (nativeEnabled(category) && habbo != null && habbo.getHabboStats() != null) {
|
||||||
|
if (category == EarningsCategory.ACHIEVEMENTS) {
|
||||||
|
int scoreStep = Math.max(1, this.config.getInt(CONFIG_PREFIX + category.getKey() + ".score.step", 100));
|
||||||
|
return "score:" + (habbo.getHabboStats().getAchievementScore() / scoreStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category == EarningsCategory.LEVEL_PROGRESS) {
|
||||||
|
int highestLevel = Math.max(habbo.getHabboStats().citizenshipLevel, habbo.getHabboStats().helpersLevel);
|
||||||
|
return "level:" + highestLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return String.valueOf(now / cooldownSeconds);
|
return String.valueOf(now / cooldownSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ For `points`, `pointsType` carries the currency type. For `badge`, `data` carrie
|
|||||||
|
|
||||||
- `marketplace`: sold marketplace offers waiting for payout
|
- `marketplace`: sold marketplace offers waiting for payout
|
||||||
- `hc_payday`: unclaimed rows in `logs_hc_payday`
|
- `hc_payday`: unclaimed rows in `logs_hc_payday`
|
||||||
|
- `achievements`: configured rewards gated by achievement score buckets
|
||||||
|
- `level_progress`: configured rewards gated by citizenship/helper talent level
|
||||||
|
|
||||||
## Result Status
|
## Result Status
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ Add emulator settings with safe defaults:
|
|||||||
|
|
||||||
The feature defaults off so existing hotels do not receive surprise economy changes after deploying the jar.
|
The feature defaults off so existing hotels do not receive surprise economy changes after deploying the jar.
|
||||||
Marketplace and HC payday default to native integrations once the feature is enabled, because both already have server-side claim ledgers.
|
Marketplace and HC payday default to native integrations once the feature is enabled, because both already have server-side claim ledgers.
|
||||||
|
Achievements and level progress use native eligibility by default: achievement score buckets and talent-track levels decide when the configured reward may be claimed.
|
||||||
|
|
||||||
## Packet Contract
|
## Packet Contract
|
||||||
|
|
||||||
@@ -94,6 +95,8 @@ Composer format is intentionally simple and renderer-friendly: category key, ena
|
|||||||
- `claim all` processes only claimable rows and returns per-category results.
|
- `claim all` processes only claimable rows and returns per-category results.
|
||||||
- Marketplace claims use the existing marketplace sold-offer payout path.
|
- Marketplace claims use the existing marketplace sold-offer payout path.
|
||||||
- HC payday claims use existing unclaimed `logs_hc_payday` rows.
|
- HC payday claims use existing unclaimed `logs_hc_payday` rows.
|
||||||
|
- Achievement claims can be scoped to score buckets via `earnings.achievements.score.step`.
|
||||||
|
- Level progress claims can be scoped to the current highest citizenship/helper level.
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user