mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-13 12:54:36 +01:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d14099f11 | ||
|
|
9599877005 | ||
|
|
31e56ae17f | ||
|
|
ab1f84e8db | ||
|
|
588522df88 | ||
|
|
660d5a3cf7 | ||
|
|
1aa2129bba | ||
|
|
968880c320 | ||
|
|
353d7d97b6 | ||
|
|
c51268bcb8 | ||
|
|
11ea526a35 | ||
|
|
5668ba9cea | ||
|
|
fef25496fa | ||
|
|
5d9ff6e1af | ||
|
|
3a6387c2bd | ||
|
|
6483e8a5a7 | ||
|
|
5d797fb9d3 | ||
|
|
0ad87dd751 | ||
|
|
5b7adc8fa4 | ||
|
|
6f832bcdfe | ||
|
|
bf5fe3912f | ||
|
|
c5f339c8be | ||
|
|
80a181680b | ||
|
|
f582444679 | ||
|
|
2af715477e | ||
|
|
9d77005da6 | ||
|
|
78d88a87cd | ||
|
|
e5ce16d6ea | ||
|
|
b92319b4c5 | ||
|
|
1b0b6873b9 | ||
|
|
f542ea7cb4 | ||
|
|
fa08bcebae | ||
|
|
209ce83fc9 | ||
|
|
8e7ef038ea | ||
|
|
90f4be862f | ||
|
|
05e74f4d12 | ||
|
|
005f138599 | ||
|
|
be5a6709fd | ||
|
|
c6ac09f112 | ||
|
|
7f0bdb1824 | ||
|
|
f8bd7d5db2 | ||
|
|
a7eddd2ed0 | ||
|
|
467b7443f3 | ||
|
|
2a7817df95 | ||
|
|
dfb93cae4b | ||
|
|
5707c1c919 | ||
|
|
51f6db9803 | ||
|
|
3df873e385 | ||
|
|
f44262f427 | ||
|
|
cf63bc0b7e | ||
|
|
b7bf1fcdeb | ||
|
|
198d3aac4f | ||
|
|
810427a028 | ||
|
|
70c7c849df | ||
|
|
0b7f1ae3a2 | ||
|
|
5182e94db7 | ||
|
|
426e5bce63 | ||
|
|
b9c4a174f8 | ||
|
|
6974631601 | ||
|
|
880f0d1d7d | ||
|
|
86c607c0b3 | ||
|
|
3710f0a697 | ||
|
|
c19aa5d0a1 | ||
|
|
be84e0f406 | ||
|
|
e5cb842fdd | ||
|
|
15618414a6 | ||
|
|
71de6184b9 | ||
|
|
e887d5eb4c | ||
|
|
211e012c42 | ||
|
|
9c87d74ad7 | ||
|
|
357d12779b | ||
|
|
c8a7db75aa | ||
|
|
ef8846445c | ||
|
|
7ef7490c37 | ||
|
|
2c1e1ae2fb | ||
|
|
e3d34bfa48 | ||
|
|
65250b07bf | ||
|
|
b38f4f0957 | ||
|
|
e4dc85a50f | ||
|
|
893b23b50d | ||
|
|
33b1cf55d4 | ||
|
|
aecea6ab03 | ||
|
|
e8e7df7d50 | ||
|
|
9188d3b53a | ||
|
|
30f565d0d6 | ||
|
|
faa4ea0780 | ||
|
|
354f390c3b | ||
|
|
3171bce3cc | ||
|
|
56b4d3b66d | ||
|
|
585734c2f3 | ||
|
|
6f7a92725a | ||
|
|
a04f3354f7 | ||
|
|
f53bdaba32 |
57
README.md
57
README.md
@@ -1,9 +1,11 @@
|
||||
# Nebula
|
||||
|
||||
A work in progress game server emulator for a certain anime game.
|
||||
A work in progress game server emulator for a certain anime game. Most features are implemented.
|
||||
|
||||
For any extra support, questions, or discussions, check out our [Discord](https://discord.gg/cskCWBqdJk).
|
||||
|
||||
Latest nightly compiled server jar: [Nightly](https://nightly.link/Melledy/Nebula/workflows/gradle/main/Nebula-Nightly.zip)
|
||||
|
||||
### Notable features
|
||||
- Basic profile features
|
||||
- Character system
|
||||
@@ -15,19 +17,25 @@ For any extra support, questions, or discussions, check out our [Discord](https:
|
||||
- Battle pass
|
||||
- Gacha
|
||||
- Friend system (sending energy not implemented)
|
||||
- Shop (using only in-game currency)
|
||||
- Shop (only in-game currency supported)
|
||||
- Commissions
|
||||
- Heartlink
|
||||
- Monoliths (completeable but many other features missing)
|
||||
- Achievements
|
||||
- Monoliths (research quests not implemented)
|
||||
- Bounty Trials
|
||||
- Menance Arena
|
||||
- Proving grounds
|
||||
- Proving Grounds
|
||||
- Catacylsm Survivor (talents not fully working)
|
||||
- Boss Blitz
|
||||
- Events (Only tower defense and trials)
|
||||
|
||||
### Not implemented
|
||||
- Achievements
|
||||
- Events
|
||||
### Supported regions
|
||||
|
||||
Nebula supports the global PC client by default. If you want to switch regions, you need to change the `region` field in the Nebula config.
|
||||
|
||||
Current supported regions (PC): `GLOBAL`, `KR`, `JP`, `TW`, `CN`
|
||||
|
||||
You may need to change the data version when switching regions. The `customDataVersion` field should match the the data version of your client, which is usually the last number of your client's version string (top left of your login screen). Example: 1.0.0.42 = data version 42.
|
||||
|
||||
# Running the server and client
|
||||
|
||||
@@ -38,7 +46,7 @@ For any extra support, questions, or discussions, check out our [Discord](https:
|
||||
* [MongoDB 4.0+](https://www.mongodb.com/try/download/community)
|
||||
|
||||
### Compiling the server
|
||||
1. Open your system terminal, and compile the server with `./gradlew jar`
|
||||
1. Open your system terminal, and compile the server with `./gradlew jar` (If you downloaded the server jar from the Nightly link, you can skip this step)
|
||||
2. Create a folder named `resources` in your server directory
|
||||
3. Download the `bin`, `language` folders from a repository with [datamined game data](https://github.com/Hiro420/StellaSoraData) and place them into your resources folder.
|
||||
4. Run the server with `java -jar Nebula.jar` from your system terminal. This server comes with a built-in internal MongoDB server for its database, so no Mongodb installation is required. However, it is highly recommended to install Mongodb anyway.
|
||||
@@ -49,17 +57,25 @@ For any extra support, questions, or discussions, check out our [Discord](https:
|
||||
3. Copy and paste the following code into the Fiddlerscript tab of Fiddler Classic. Remember to save the fiddler script after you copy and paste it:
|
||||
|
||||
```
|
||||
import System;
|
||||
import System.Windows.Forms;
|
||||
import Fiddler;
|
||||
import System.Text.RegularExpressions;
|
||||
|
||||
class Handlers
|
||||
{
|
||||
static var list = [
|
||||
".yostarplat.com",
|
||||
".stellasora.global",
|
||||
".stellasora.kr",
|
||||
".stellasora.jp",
|
||||
".stargazer-games.com",
|
||||
".yostar.cn"
|
||||
];
|
||||
|
||||
static function OnBeforeRequest(oS: Session) {
|
||||
if (oS.host.EndsWith(".yostarplat.com") || oS.host.EndsWith(".stellasora.global")) {
|
||||
oS.oRequest.headers.UriScheme = "http";
|
||||
oS.host = "localhost"; // This can also be replaced with another IP address.
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (oS.host.EndsWith(list[i])) {
|
||||
oS.oRequest.headers.UriScheme = "http";
|
||||
oS.host = "localhost"; // This can also be replaced with another IP address
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -68,25 +84,20 @@ class Handlers
|
||||
4. If `autoCreateAccount` is set to true in the config, then you can skip this step. Otherwise, type `/account create [account email]` in the server console to create an account.
|
||||
5. Login with your account email, the code field is ignored by the server and can be set to anything.
|
||||
|
||||
If you are not on the global client, `.stellasora.global` in the fiddlerscript may need to be changed to match the endpoint your client connects to.
|
||||
|
||||
### Supported regions
|
||||
|
||||
Nebula supports the global client by default. If you want to switch regions, you need to change the `customDataVersion` and `region` fields in the Nebula config. The `customDataVersion` field should match the the data version of your client, which is usually the last number of your client's version string (top left of your login screen). Example: 1.0.0.42 = data version 42.
|
||||
|
||||
Current supported regions: `global`, `kr`
|
||||
|
||||
### Server commands
|
||||
Server commands need to be run in the server console OR in the signature edit menu of your profile.
|
||||
|
||||
```
|
||||
!account {create | delete} [email] (reserved player uid) = Creates or deletes an account.
|
||||
!battlepass [free | premium] lv(level) = Modifies the targeted player's battle pass
|
||||
!char [all | {characterId}] lv(level) a(ascension) s(skill level) t(talent level) f(affinity level) = Changes the properties of the targeted characters.
|
||||
!clean [all | {id} ...] [items|resources] = Removes items/resources from the targeted player.
|
||||
!disc [all | {discId}] lv(level) a(ascension) c(crescendo level) = Changes the properties of the targeted discs.
|
||||
!give [item id] x[amount] = Gives the targeted player an item through the mail.
|
||||
!giveall [characters | discs | materials] = Gives the targeted player items.
|
||||
!help = Displays a list of available commands. (Very spammy in-game)
|
||||
!level (level) = Sets the player level
|
||||
!mail = Sends the targeted player a system mail.
|
||||
!mail "subject" "body" [itemId xQty | itemId:qty ...] = Sends the targeted player a system mail.
|
||||
!reload = Reloads the server config.
|
||||
!remote = Creates a player token for remote api usage
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
version = '1.1.1'
|
||||
version = '1.1.3'
|
||||
|
||||
var shouldGenerateProto = System.getenv("GENERATE_PROTO") == "true"
|
||||
System.out.println(shouldGenerateProto ? "Generating proto files" : "Skipping proto generation")
|
||||
|
||||
@@ -101,14 +101,21 @@ public class Config {
|
||||
|
||||
@Getter
|
||||
public static class ServerOptions {
|
||||
// Default permissions for accounts. By default, all commands are allowed. Reccomended to change if making a public server.
|
||||
public Set<String> defaultPermissions = Set.of("*");
|
||||
// Automatically creates an account when a player logs in for the first time on a new email.
|
||||
public boolean autoCreateAccount = true;
|
||||
// Skips the intro cinematics/stage when starting a new account.
|
||||
public boolean skipIntro = false;
|
||||
// Unlocks all instances (Monolith, Bounty Trials, etc) for players to enter without needing to do the previous levels.
|
||||
public boolean unlockInstances = true;
|
||||
public int sessionTimeout = 600; // How long to wait (in seconds) after the last http request from a session
|
||||
// before removing it from the server
|
||||
// How long to wait (in seconds) after the last http request from a session before removing it from the server.
|
||||
public int sessionTimeout = 300;
|
||||
// The offset hour for when daily quests are refreshed in UTC. Example: "dailyResetHour = 4" means dailies will be refreshed at UTC+4 12:00 AM every day.
|
||||
public int dailyResetHour = 0;
|
||||
public int leaderboardRefreshTime = 60; // Leaderboard refresh time in seconds
|
||||
// Leaderboard for Boss Blitz refresh time in seconds.
|
||||
public int leaderboardRefreshTime = 60;
|
||||
// The welcome mail to send when a player is created. Set to null to disable.
|
||||
public WelcomeMail welcomeMail = new WelcomeMail();
|
||||
}
|
||||
|
||||
@@ -121,6 +128,7 @@ public class Config {
|
||||
public static class LogOptions {
|
||||
public boolean commands = true;
|
||||
public boolean packets = false;
|
||||
public boolean httpDebug = false;
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -6,8 +6,26 @@ import emu.nebula.game.inventory.ItemParam;
|
||||
import emu.nebula.util.WeightedList;
|
||||
|
||||
public class GameConstants {
|
||||
private static final int DATA_VERSION = 54;
|
||||
private static final String VERSION = "1.2.0";
|
||||
public static final String VERSION = "1.3.0";
|
||||
public static int DATA_VERSION = 0;
|
||||
|
||||
// Set data versions for each region
|
||||
static {
|
||||
RegionConfig.getRegion("global")
|
||||
.setDataVersion(69);
|
||||
|
||||
RegionConfig.getRegion("kr")
|
||||
.setDataVersion(76);
|
||||
|
||||
RegionConfig.getRegion("jp")
|
||||
.setDataVersion(72);
|
||||
|
||||
RegionConfig.getRegion("tw")
|
||||
.setDataVersion(70);
|
||||
|
||||
RegionConfig.getRegion("cn")
|
||||
.setDataVersion(70);
|
||||
}
|
||||
|
||||
public static final ZoneId UTC_ZONE = ZoneId.of("UTC");
|
||||
|
||||
@@ -19,8 +37,8 @@ public class GameConstants {
|
||||
public static final int GEM_ITEM_ID = 2;
|
||||
public static final int PREM_GEM_ITEM_ID = 3;
|
||||
public static final int ENERGY_BUY_ITEM_ID = GEM_ITEM_ID;
|
||||
public static final int STAR_TOWER_GOLD_ITEM_ID = 11;
|
||||
public static final int EXP_ITEM_ID = 21;
|
||||
public static final int WEEKLY_ENTRY_ITEM_ID = 28;
|
||||
|
||||
public static final int MAX_ENERGY = 240;
|
||||
public static final int ENERGY_REGEN_TIME = 360; // Seconds
|
||||
@@ -37,6 +55,14 @@ public class GameConstants {
|
||||
public static final int MAX_FRIENDSHIPS = 50;
|
||||
public static final int MAX_PENDING_FRIENDSHIPS = 30;
|
||||
|
||||
public static final int TOWER_COIN_ITEM_ID = 11;
|
||||
public static final int[] TOWER_COMMON_SUB_NOTE_SKILLS = new int[] {
|
||||
90011, 90012, 90013, 90014, 90015, 90016, 90017
|
||||
};
|
||||
public static final int[] TOWER_EVENTS_IDS = new int[] {
|
||||
101, 102, 104, 105, 106, 107, 108, 114, 115, 116, 126, 127, 128
|
||||
};
|
||||
|
||||
public static int[][] VAMPIRE_SURVIVOR_BONUS_POWER = new int[][] {
|
||||
new int[] {100, 120},
|
||||
new int[] {200, 150},
|
||||
@@ -57,7 +83,14 @@ public class GameConstants {
|
||||
// Helper functions
|
||||
|
||||
public static String getGameVersion() {
|
||||
return VERSION + "." + getDataVersion() + " (" + Nebula.getConfig().getRegion().toUpperCase() + ")";
|
||||
// Load data version
|
||||
var region = RegionConfig.getRegion(Nebula.getConfig().getRegion());
|
||||
|
||||
// Set data version from region
|
||||
GameConstants.DATA_VERSION = region.getDataVersion();
|
||||
|
||||
// Init game version string
|
||||
return VERSION + "." + getDataVersion() + " (" + region.getName().toUpperCase() + ")";
|
||||
}
|
||||
|
||||
public static int getDataVersion() {
|
||||
|
||||
@@ -43,9 +43,8 @@ public class Nebula {
|
||||
@Getter private static PluginManager pluginManager;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Load config + keys first
|
||||
// Load config first
|
||||
Nebula.loadConfig();
|
||||
AeadHelper.loadKeys();
|
||||
|
||||
// Start Server
|
||||
Nebula.getLogger().info("Starting Nebula " + getJarVersion());
|
||||
@@ -54,6 +53,9 @@ public class Nebula {
|
||||
|
||||
boolean generateHandbook = true;
|
||||
|
||||
// Load keys
|
||||
AeadHelper.loadKeys();
|
||||
|
||||
// Load plugin manager
|
||||
Nebula.pluginManager = new PluginManager();
|
||||
|
||||
|
||||
42
src/main/java/emu/nebula/RegionConfig.java
Normal file
42
src/main/java/emu/nebula/RegionConfig.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package emu.nebula;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter @Setter
|
||||
public class RegionConfig {
|
||||
private String name;
|
||||
private int dataVersion;
|
||||
private String serverMetaKey;
|
||||
private String serverGarbleKey;
|
||||
|
||||
private static Object2ObjectMap<String, RegionConfig> REGIONS = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public RegionConfig(String name) {
|
||||
this.name = name;
|
||||
this.serverMetaKey = "";
|
||||
this.serverGarbleKey = "";
|
||||
}
|
||||
|
||||
public RegionConfig setDataVersion(int i) {
|
||||
this.dataVersion = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegionConfig setServerMetaKey(String key) {
|
||||
this.serverMetaKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegionConfig setServerGarbleKey(String key) {
|
||||
this.serverGarbleKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static RegionConfig getRegion(String name) {
|
||||
String regionName = name.toLowerCase();
|
||||
return REGIONS.computeIfAbsent(regionName, r -> new RegionConfig(regionName));
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.character.GameDisc;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import lombok.Getter;
|
||||
@@ -53,6 +53,9 @@ public class CommandArgs {
|
||||
} else if (arg.startsWith("lv")) { // Level
|
||||
this.level = Utils.parseSafeInt(arg.substring(2));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("lvl")) { // Level
|
||||
this.level = Utils.parseSafeInt(arg.substring(3));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("a")) { // Advance
|
||||
this.advance = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
@@ -76,7 +79,7 @@ public class CommandArgs {
|
||||
int key = Integer.parseInt(split[0]);
|
||||
int value = Integer.parseInt(split[1]);
|
||||
|
||||
if (this.map == null) this.map = new Int2IntOpenHashMap();
|
||||
if (this.map == null) this.map = new Int2IntLinkedOpenHashMap();
|
||||
this.map.put(key, value);
|
||||
|
||||
it.remove();
|
||||
|
||||
@@ -6,7 +6,7 @@ import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.game.account.AccountHelper;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
@Command(label = "account", permission = "admin.account", desc = "/account {create | delete} [email] (reserved player uid). Creates or deletes an account.")
|
||||
@Command(label = "account", permission = "admin.account", desc = "!account {create | delete} [email] (reserved player uid). Creates or deletes an account.")
|
||||
public class AccountCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
||||
115
src/main/java/emu/nebula/command/commands/BanCommand.java
Normal file
115
src/main/java/emu/nebula/command/commands/BanCommand.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Command(label = "ban",
|
||||
permission = "admin.ban",
|
||||
desc = """
|
||||
!ban {all | ip | uid} [player uid | ip] (end timestamp) (reason) - Ban a player\
|
||||
|
||||
- all mode bans both the player object and their IP address by UID, so the next parameter should be UID instead of IP
|
||||
- ip mode can only ban IP addresses, so the next parameter should be an IP
|
||||
- uid mode can only ban UIDs, so the next parameter should be a UID
|
||||
- If you don't fill in the end timestamp, it will be permanently banned by default\
|
||||
""")
|
||||
public class BanCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
if (args.size() < 2) {
|
||||
return "Invalid amount of args";
|
||||
}
|
||||
|
||||
int bannedUid = 0;
|
||||
long banEndTime = 0;
|
||||
String bannedIp = null;
|
||||
String reason = null;
|
||||
|
||||
String mode = args.get(0).toLowerCase(Locale.ROOT);
|
||||
|
||||
switch (args.size()) {
|
||||
case 4:
|
||||
reason = args.get(3);
|
||||
case 3: {
|
||||
try {
|
||||
banEndTime = Long.parseLong(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "Unable to parse timestamp.";
|
||||
}
|
||||
}
|
||||
case 2: {
|
||||
if (mode.equals("ip")) {
|
||||
bannedIp = args.get(1);
|
||||
} else {
|
||||
try {
|
||||
bannedUid = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "Unable to parse uid.";
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1: {
|
||||
if (!mode.equals("all") && !mode.equals("uid") && !mode.equals("ip"))
|
||||
return "Unable to parse mode.";
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (banEndTime != 0 && banEndTime < System.currentTimeMillis()) {
|
||||
return "Failed, the end timestamp must be greater than the current time";
|
||||
}
|
||||
|
||||
var banModule = Nebula.getGameContext().getBanModule();
|
||||
|
||||
Player player;
|
||||
if (!mode.equals("ip")) {
|
||||
player = Nebula.getGameContext().getPlayerModule().getPlayer(bannedUid);
|
||||
|
||||
if (player == null) {
|
||||
return "Failed, player not found.";
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case "all" -> {
|
||||
banModule.banPlayer(
|
||||
bannedUid,
|
||||
banEndTime,
|
||||
reason,
|
||||
true,
|
||||
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
|
||||
return "Banned player all mode " + bannedUid + " until " + Utils.formatTimestamp(banEndTime) +
|
||||
(reason != null ? " (" + reason + ")" : "");
|
||||
}
|
||||
case "ip" -> {
|
||||
banModule.banIp(
|
||||
bannedIp,
|
||||
banEndTime,
|
||||
reason,
|
||||
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
|
||||
return "Banned ip " + bannedIp + " until " + Utils.formatTimestamp(banEndTime) +
|
||||
(reason != null ? " (" + reason + ")" : "");
|
||||
}
|
||||
case "uid" -> {
|
||||
banModule.banPlayer(
|
||||
bannedUid,
|
||||
banEndTime,
|
||||
reason,
|
||||
false,
|
||||
args.getSender() != null ? String.valueOf(args.getSender().getUid()) : "Console");
|
||||
return "Banned player " + bannedUid + " until " + Utils.formatTimestamp(banEndTime) +
|
||||
(reason != null ? " (" + reason + ")" : "");
|
||||
}
|
||||
default -> {
|
||||
return "Ban sub command not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
|
||||
@Command(label = "battlepass", aliases = {"bp"}, permission = "player.battlepass", desc = "!battlepass [free | premium] lv(level) = Modifies the targeted player's battle pass")
|
||||
public class BattlePassCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
// Get target
|
||||
var target = args.getTarget();
|
||||
var battlepass = target.getBattlePassManager().getBattlePass();
|
||||
boolean changed = false;
|
||||
|
||||
// Check if we are changing premium status
|
||||
int mode = -1;
|
||||
|
||||
for (var arg : args.getList()) {
|
||||
if (arg.equalsIgnoreCase("free")) {
|
||||
mode = 0;
|
||||
} else if (arg.equalsIgnoreCase("premium")) {
|
||||
mode = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode >= 0 && battlepass.getMode() != mode) {
|
||||
battlepass.setMode(mode);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Set level
|
||||
int level = Math.min(args.getLevel(), 50);
|
||||
|
||||
if (level >= 0 && battlepass.getLevel() != level) {
|
||||
battlepass.setLevel(level);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Check if we have made any changes
|
||||
if (changed) {
|
||||
// Save battle pass to the database
|
||||
battlepass.save();
|
||||
|
||||
// Send package to notify the client that the battle pass needs updating
|
||||
target.addNextPackage(
|
||||
NetMsgId.battle_pass_info_succeed_ack,
|
||||
battlepass.toProto()
|
||||
);
|
||||
|
||||
// Success message
|
||||
return "Changed the battle pass successfully.";
|
||||
}
|
||||
|
||||
// Result message
|
||||
return "No changes were made to the battle pass.";
|
||||
}
|
||||
|
||||
}
|
||||
182
src/main/java/emu/nebula/command/commands/BuildCommand.java
Normal file
182
src/main/java/emu/nebula/command/commands/BuildCommand.java
Normal file
@@ -0,0 +1,182 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.character.GameDisc;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.tower.StarTowerBuild;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Command(
|
||||
label = "build",
|
||||
aliases = {"b", "record", "r"},
|
||||
permission = "player.build",
|
||||
requireTarget = true,
|
||||
desc = "!build [char ids...] [disc ids...] [potential ids...] [melody ids...]"
|
||||
)
|
||||
public class BuildCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
// Create record
|
||||
var target = args.getTarget();
|
||||
var builder = new StarTowerBuildData(target);
|
||||
|
||||
// Parse items
|
||||
for (String arg : args.getList()) {
|
||||
int id = Utils.parseSafeInt(arg);
|
||||
int count = 1;
|
||||
|
||||
this.parseItem(builder, id, count);
|
||||
}
|
||||
|
||||
if (args.getMap() != null) {
|
||||
for (var entry : args.getMap().int2IntEntrySet()) {
|
||||
int id = entry.getIntKey();
|
||||
int count = entry.getIntValue();
|
||||
|
||||
this.parseItem(builder, id, count);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if build is valid
|
||||
if (builder.getCharacters().size() != 3) {
|
||||
return "Record must have 3 different characters";
|
||||
}
|
||||
|
||||
if (builder.getDiscs().size() < 3 || builder.getDiscs().size() > 6) {
|
||||
return "Record must have 3-6 different discs";
|
||||
}
|
||||
|
||||
// Create record
|
||||
var build = builder.toBuild();
|
||||
|
||||
// Add to star tower manager
|
||||
target.getStarTowerManager().getBuilds().put(build.getUid(), build);
|
||||
|
||||
// Save to database
|
||||
build.save();
|
||||
|
||||
// Send package to player
|
||||
target.addNextPackage(NetMsgId.st_import_build_notify, build.toProto());
|
||||
|
||||
// Send result to player
|
||||
return "Created record for " + target.getName() + " (This command make take time to update on the client)";
|
||||
}
|
||||
|
||||
private void parseItem(StarTowerBuildData builder, int id, int count) {
|
||||
// Get item data
|
||||
var itemData = GameData.getItemDataTable().get(id);
|
||||
if (itemData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clamp
|
||||
count = Math.max(count, 1);
|
||||
|
||||
// Parse by item id
|
||||
switch (itemData.getItemSubType()) {
|
||||
case Char -> {
|
||||
var character = builder.getPlayer().getCharacters().getCharacterById(id);
|
||||
if (character == null || !character.getData().isAvailable()) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.addCharacter(character);
|
||||
}
|
||||
case Disc -> {
|
||||
var disc = builder.getPlayer().getCharacters().getDiscById(id);
|
||||
if (disc == null || !disc.getData().isAvailable()) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.addDisc(disc);
|
||||
}
|
||||
case Potential, SpecificPotential -> {
|
||||
var potentialData = GameData.getPotentialDataTable().get(id);
|
||||
if (potentialData == null) break;
|
||||
|
||||
int level = Math.min(count, potentialData.getMaxLevel());
|
||||
builder.getBuild().getPotentials().add(id, level);
|
||||
}
|
||||
case SubNoteSkill -> {
|
||||
builder.getBuild().getSubNoteSkills().add(id, count);
|
||||
}
|
||||
default -> {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static class StarTowerBuildData {
|
||||
private Player player;
|
||||
private StarTowerBuild build;
|
||||
private List<GameCharacter> characters;
|
||||
private List<GameDisc> discs;
|
||||
|
||||
public StarTowerBuildData(Player player) {
|
||||
this.player = player;
|
||||
this.build = new StarTowerBuild(player);
|
||||
this.characters = new ArrayList<>();
|
||||
this.discs = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void addCharacter(GameCharacter character) {
|
||||
if (this.characters.contains(character)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.characters.add(character);
|
||||
}
|
||||
|
||||
public void addDisc(GameDisc disc) {
|
||||
if (this.discs.contains(disc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.discs.add(disc);
|
||||
}
|
||||
|
||||
public StarTowerBuild toBuild() {
|
||||
// Set characters and discs
|
||||
build.setChars(this.getCharacters());
|
||||
build.setDiscs(this.getDiscs());
|
||||
|
||||
// Clear character potential cache
|
||||
build.getCharPots().clear();
|
||||
|
||||
for (int charId : build.getCharIds()) {
|
||||
build.getCharPots().put(charId, 0);
|
||||
}
|
||||
|
||||
// Add potentials to character potential cache
|
||||
var it = build.getPotentials().iterator();
|
||||
while (it.hasNext()) {
|
||||
var potential = it.next();
|
||||
|
||||
var data = GameData.getPotentialDataTable().get(potential.getIntKey());
|
||||
if (data == null || !build.getCharPots().containsKey(data.getCharId())) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
build.getCharPots().add(data.getCharId(), 1);
|
||||
}
|
||||
|
||||
// Calculate score
|
||||
build.calculateScore();
|
||||
|
||||
// Return build
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import java.util.HashSet;
|
||||
aliases = {"cl", "clear"},
|
||||
permission = "player.inventory",
|
||||
requireTarget = true,
|
||||
desc = "!clean [all | {id} ...] [items|resources]. Removes items/resources from the targeted player."
|
||||
desc = "!clean [all | {id} ...] [items|resources] = Removes items/resources from the targeted player."
|
||||
)
|
||||
public class CleanCommand implements CommandHandler {
|
||||
|
||||
|
||||
@@ -84,7 +84,11 @@ public class GiveAllCommand implements CommandHandler {
|
||||
var disc = target.getCharacters().addDisc(data.getId());
|
||||
|
||||
// Set properties
|
||||
args.setProperties(disc);
|
||||
boolean shouldSave = args.setProperties(disc);
|
||||
|
||||
if (shouldSave) {
|
||||
disc.save();
|
||||
}
|
||||
|
||||
// Add to change info
|
||||
change.add(disc.toProto());
|
||||
@@ -110,7 +114,11 @@ public class GiveAllCommand implements CommandHandler {
|
||||
var character = target.getCharacters().addCharacter(data.getId());
|
||||
|
||||
// Set properties
|
||||
args.setProperties(character);
|
||||
boolean shouldSave = args.setProperties(character);
|
||||
|
||||
if (shouldSave) {
|
||||
character.save();
|
||||
}
|
||||
|
||||
// Add to change info
|
||||
change.add(character.toProto());
|
||||
|
||||
@@ -12,7 +12,7 @@ import emu.nebula.command.CommandHandler;
|
||||
aliases = {"g", "item"},
|
||||
permission = "player.give",
|
||||
requireTarget = true,
|
||||
desc = "/give [item id] x(amount). Gives the targeted player an item."
|
||||
desc = "!give [item id] x(amount). Gives the targeted player an item."
|
||||
)
|
||||
public class GiveCommand implements CommandHandler {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
|
||||
@Command(label = "help", permission = "player.help", desc = "/help. Displays a list of available commands.")
|
||||
@Command(label = "help", permission = "player.help", desc = "!help = Displays a list of available commands. (Very spammy in-game)")
|
||||
public class HelpCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -8,8 +8,8 @@ import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.NotifyGm.GmWorldClass;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
@Command(label = "setlevel", aliases = {"level", "l"}, permission = "player.level", requireTarget = true, desc = "/level [level]. Set player level")
|
||||
public class SetLevelCommand implements CommandHandler {
|
||||
@Command(label = "setlevel", aliases = {"level", "l"}, permission = "player.level", requireTarget = true, desc = "!level [level] = Set's the targeted player's level")
|
||||
public class LevelCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
@@ -14,7 +14,7 @@ import emu.nebula.util.Utils;
|
||||
aliases = {"m"},
|
||||
permission = "player.mail",
|
||||
requireTarget = true,
|
||||
desc = "/mail \"subject\" \"body\" [itemId xQty | itemId:qty ...]. Sends the targeted player a system mail."
|
||||
desc = "!mail \"subject\" \"body\" [itemId xQty | itemId:qty ...] = Sends the targeted player a system mail."
|
||||
)
|
||||
public class MailCommand implements CommandHandler {
|
||||
private static final String USAGE_TEXT = "Usage: /mail \"subject\" \"body\" [itemId xQty | itemId:qty ...]";
|
||||
|
||||
@@ -5,7 +5,7 @@ import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
|
||||
@Command(label = "reload", permission = "admin.reload", desc = "/reload. Reloads the server config.")
|
||||
@Command(label = "reload", permission = "admin.reload", desc = "!reload = Reloads the server config.")
|
||||
public class ReloadCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,7 +7,7 @@ import emu.nebula.command.CommandHandler;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Command(label = "remote", permission = "player.remote", requireTarget = true, desc = "/remote. Send remote to web remote")
|
||||
@Command(label = "remote", permission = "player.remote", requireTarget = true, desc = "!remote = Creates a player token for remote api usage")
|
||||
public class RemoteKeyCommand implements CommandHandler {
|
||||
private final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
|
||||
101
src/main/java/emu/nebula/command/commands/StatusCommand.java
Normal file
101
src/main/java/emu/nebula/command/commands/StatusCommand.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
@Command(label = "status", permission = "admin.status", desc = "!status = Displays server runtime status.")
|
||||
public class StatusCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (args.getRaw().contains("@")) {
|
||||
if (args.getTarget() == null || !args.getTarget().hasSession()) {
|
||||
return "Error - Targeted player not found or offline";
|
||||
}
|
||||
}
|
||||
|
||||
var runtime = Runtime.getRuntime();
|
||||
var uptimeMs = ManagementFactory.getRuntimeMXBean().getUptime();
|
||||
|
||||
double usedMem = (runtime.totalMemory() - runtime.freeMemory()) / 1048576.0;
|
||||
double maxMem = runtime.maxMemory() / 1048576.0;
|
||||
|
||||
int players = 0;
|
||||
if (Nebula.getGameContext() != null) {
|
||||
players = Nebula.getGameContext().getPlayerModule().getCachedPlayers().size();
|
||||
}
|
||||
|
||||
var http = Nebula.getHttpServer();
|
||||
String addr = "-";
|
||||
String scheme = "http";
|
||||
int port = 0;
|
||||
if (http != null) {
|
||||
addr = http.getServerConfig().getPublicAddress();
|
||||
port = http.getServerConfig().getPublicPort();
|
||||
scheme = http.getServerConfig().isUseSSL() ? "https" : "http";
|
||||
}
|
||||
|
||||
sb.append("Server Status\n");
|
||||
sb.append("Git: ").append(Nebula.getGitHash()).append('\n');
|
||||
sb.append("Game: ").append(GameConstants.getGameVersion()).append('\n');
|
||||
sb.append("HTTP: ").append(scheme).append("://").append(addr).append(":").append(port).append('\n');
|
||||
sb.append("Uptime: ").append(formatUptime(uptimeMs)).append('\n');
|
||||
sb.append("CPU: ").append(getCpu()).append('\n');
|
||||
sb.append("Load: ").append(getLoad()).append('\n');
|
||||
sb.append("Disk: ").append(getDisk()).append('\n');
|
||||
sb.append("Memory: ").append(String.format("%.1fMB (max %.1fMB)", usedMem, maxMem)).append('\n');
|
||||
sb.append("Players: ").append(players);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static String formatUptime(long ms) {
|
||||
long s = ms / 1000;
|
||||
long h = s / 3600;
|
||||
long m = (s % 3600) / 60;
|
||||
long sec = s % 60;
|
||||
return String.format("%02dh:%02dm:%02ds", h, m, sec);
|
||||
}
|
||||
|
||||
private static String getCpu() {
|
||||
var os = ManagementFactory.getOperatingSystemMXBean();
|
||||
double v = -1;
|
||||
if (os instanceof com.sun.management.OperatingSystemMXBean) {
|
||||
v = ((com.sun.management.OperatingSystemMXBean) os).getProcessCpuLoad();
|
||||
}
|
||||
return v >= 0 ? String.format("%.1f%%", v * 100.0) : "-";
|
||||
}
|
||||
|
||||
private static String getLoad() {
|
||||
var os = ManagementFactory.getOperatingSystemMXBean();
|
||||
double v = -1;
|
||||
if (os instanceof com.sun.management.OperatingSystemMXBean csm) {
|
||||
v = csm.getCpuLoad();
|
||||
}
|
||||
if (v < 0) {
|
||||
double avg = os.getSystemLoadAverage();
|
||||
if (avg > 0) {
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
v = Math.min(1.0, avg / cores);
|
||||
}
|
||||
}
|
||||
return v >= 0 ? String.format("%.1f%%", v * 100.0) : "-";
|
||||
}
|
||||
|
||||
private static String getDisk() {
|
||||
var root = new File(".");
|
||||
long total = root.getTotalSpace();
|
||||
long usable = root.getUsableSpace();
|
||||
if (total <= 0) return "-";
|
||||
double usedPercent = (double) (total - usable) / (double) total * 100.0;
|
||||
return String.format("%.1f%%", usedPercent);
|
||||
}
|
||||
}
|
||||
81
src/main/java/emu/nebula/command/commands/UnbanCommand.java
Normal file
81
src/main/java/emu/nebula/command/commands/UnbanCommand.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.game.ban.BanInfo;
|
||||
import emu.nebula.game.player.Player;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@Command(label = "unban", permission = "admin.ban", desc = """
|
||||
!unban {all | ip | uid} [player uid | ip]\
|
||||
|
||||
- all mode unbans both the player object and their IP address by UID, so the next parameter should be UID instead of IP
|
||||
- ip mode can only unban IP addresses, so the next parameter should be an IP
|
||||
- uid mode can only unban UIDs, so the next parameter should be a UID\
|
||||
""")
|
||||
public class UnbanCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
if (args.size() < 2) {
|
||||
return "Invalid amount of args";
|
||||
}
|
||||
|
||||
int unbannedUid = 0;
|
||||
String unbannedIp = null;
|
||||
|
||||
String mode = args.get(0).toLowerCase(Locale.ROOT);
|
||||
|
||||
if (!mode.equals("all") && !mode.equals("uid") && !mode.equals("ip"))
|
||||
return "Unable to parse mode.";
|
||||
|
||||
if (mode.equals("ip")) {
|
||||
unbannedIp = args.get(1);
|
||||
} else {
|
||||
try {
|
||||
unbannedUid = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
return "Unable to parse uid.";
|
||||
}
|
||||
}
|
||||
|
||||
var banModule = Nebula.getGameContext().getBanModule();
|
||||
|
||||
Player player = null;
|
||||
if (!mode.equals("ip")) {
|
||||
player = Nebula.getGameContext().getPlayerModule().getPlayer(unbannedUid);
|
||||
|
||||
if (player == null) {
|
||||
return "Failed, player not found.";
|
||||
}
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case "all" -> {
|
||||
BanInfo banInfo = banModule.getPlayerBanInfo(player.getUid());
|
||||
|
||||
banModule.unbanPlayer(player.getUid());
|
||||
|
||||
if (banInfo != null) {
|
||||
unbannedIp = banInfo.getIpAddress();
|
||||
if (unbannedIp != null)
|
||||
banModule.unbanIp(unbannedIp);
|
||||
}
|
||||
return "Unban a player all mode " + unbannedUid;
|
||||
} case "uid" -> {
|
||||
banModule.unbanPlayer(player.getUid());
|
||||
return "Unban a player " + unbannedUid;
|
||||
} case "ip" -> {
|
||||
banModule.unbanIp(unbannedIp);
|
||||
return "Unban a ip " + unbannedIp;
|
||||
}
|
||||
default -> {
|
||||
// Fallback
|
||||
return "Unban sub command not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,14 @@ import java.util.stream.Collectors;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
import emu.nebula.data.custom.CharGemAttrGroupDef;
|
||||
import emu.nebula.data.resources.*;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GameData {
|
||||
// Characters
|
||||
// ===== Characters =====
|
||||
@Getter private static DataTable<CharacterDef> CharacterDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharacterAdvanceDef> CharacterAdvanceDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharacterSkillUpgradeDef> CharacterSkillUpgradeDataTable = new DataTable<>();
|
||||
@@ -28,38 +28,43 @@ public class GameData {
|
||||
@Getter private static DataTable<TalentGroupDef> TalentGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<TalentDef> TalentDataTable = new DataTable<>();
|
||||
|
||||
// Character emblems
|
||||
// Characters: Emblems
|
||||
@Getter private static DataTable<CharGemDef> CharGemDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharGemSlotControlDef> CharGemSlotControlDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharGemAttrGroupDef> CharGemAttrGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharGemAttrValueDef> CharGemAttrValueDataTable = new DataTable<>();
|
||||
|
||||
// Character affinity
|
||||
// Characters: Affinity
|
||||
@Getter private static DataTable<AffinityLevelDef> AffinityLevelDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<AffinityGiftDef> AffinityGiftDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<PlotDef> PlotDataTable = new DataTable<>();
|
||||
|
||||
// Characters: Phone
|
||||
@Getter private static DataTable<ChatDef> ChatDataTable = new DataTable<>();
|
||||
|
||||
// Characters: Dating
|
||||
@Getter private static DataTable<DatingLandmarkDef> DatingLandmarkDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DatingLandmarkEventDef> DatingLandmarkEventDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DatingCharacterEventDef> DatingCharacterEventDataTable = new DataTable<>();
|
||||
|
||||
// Discs
|
||||
// ===== Discs =====
|
||||
@Getter private static DataTable<DiscDef> DiscDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscStrengthenDef> DiscStrengthenDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscItemExpDef> DiscItemExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscPromoteDef> DiscPromoteDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscPromoteLimitDef> DiscPromoteLimitDataTable = new DataTable<>();
|
||||
|
||||
// Items
|
||||
// Discs: Melody items
|
||||
@Getter private static DataTable<SecondarySkillDef> SecondarySkillDataTable = new DataTable<>();
|
||||
|
||||
// ===== Items =====
|
||||
@Getter private static DataTable<ItemDef> ItemDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ProductionDef> ProductionDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<PlayerHeadDef> PlayerHeadDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<TitleDef> titleDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<HonorDef> honorDataTable = new DataTable<>();
|
||||
|
||||
// Shops
|
||||
// ===== Shops =====
|
||||
@Getter private static DataTable<MallMonthlyCardDef> MallMonthlyCardDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<MallPackageDef> MallPackageDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<MallShopDef> MallShopDataTable = new DataTable<>();
|
||||
@@ -68,20 +73,39 @@ public class GameData {
|
||||
@Getter private static DataTable<ResidentShopDef> ResidentShopDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ResidentGoodsDef> ResidentGoodsDataTable = new DataTable<>();
|
||||
|
||||
// Battle Pass
|
||||
// ===== Battle Pass =====
|
||||
@Getter private static DataTable<BattlePassDef> BattlePassDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<BattlePassLevelDef> BattlePassLevelDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<BattlePassQuestDef> BattlePassQuestDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<BattlePassRewardDef> BattlePassRewardDataTable = new DataTable<>();
|
||||
|
||||
// Commissions
|
||||
// ===== Commissions =====
|
||||
@Getter private static DataTable<AgentDef> AgentDataTable = new DataTable<>();
|
||||
|
||||
// Dictionary
|
||||
// ===== Dictionary =====
|
||||
@Getter private static DataTable<DictionaryTabDef> DictionaryTabDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DictionaryEntryDef> DictionaryEntryDataTable = new DataTable<>();
|
||||
|
||||
// Instances
|
||||
// ===== Gacha =====
|
||||
@Getter private static DataTable<GachaDef> GachaDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GachaStorageDef> GachaStorageDataTable = new DataTable<>();
|
||||
|
||||
// ===== Story =====
|
||||
@Getter private static DataTable<StoryDef> StoryDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StorySetSectionDef> StorySetSectionDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StoryEvidenceDef> StoryEvidenceDataTable = new DataTable<>();
|
||||
|
||||
// ===== Daily Quests =====
|
||||
@Getter private static DataTable<DailyQuestDef> DailyQuestDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DailyQuestActiveDef> DailyQuestActiveDataTable = new DataTable<>();
|
||||
|
||||
// ===== Achievements =====
|
||||
@Getter private static DataTable<AchievementDef> AchievementDataTable = new DataTable<>();
|
||||
|
||||
// ===== Tutorials =====
|
||||
@Getter private static DataTable<TutorialLevelDef> TutorialLevelDataTable = new DataTable<>();
|
||||
|
||||
// ===== Instances =====
|
||||
@Getter private static DataTable<DailyInstanceDef> DailyInstanceDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DailyInstanceRewardGroupDef> DailyInstanceRewardGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<RegionBossLevelDef> RegionBossLevelDataTable = new DataTable<>();
|
||||
@@ -89,46 +113,60 @@ public class GameData {
|
||||
@Getter private static DataTable<CharGemInstanceDef> CharGemInstanceDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<WeekBossLevelDef> WeekBossLevelDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<GachaDef> GachaDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GachaStorageDef> GachaStorageDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<WorldClassDef> WorldClassDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GuideGroupDef> GuideGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<HandbookDef> HandbookDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StoryDef> StoryDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StorySetSectionDef> StorySetSectionDataTable = new DataTable<>();
|
||||
|
||||
// Daily quests
|
||||
@Getter private static DataTable<DailyQuestDef> DailyQuestDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DailyQuestActiveDef> DailyQuestActiveDataTable = new DataTable<>();
|
||||
|
||||
// Achievements
|
||||
@Getter private static DataTable<AchievementDef> AchievementDataTable = new DataTable<>();
|
||||
|
||||
// Tutorial
|
||||
@Getter private static DataTable<TutorialLevelDef> TutorialLevelDataTable = new DataTable<>();
|
||||
|
||||
// Star tower
|
||||
// ===== Star Tower =====
|
||||
@Getter private static DataTable<StarTowerDef> StarTowerDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerStageDef> StarTowerStageDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerGrowthNodeDef> StarTowerGrowthNodeDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerFloorExpDef> StarTowerFloorExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerTeamExpDef> StarTowerTeamExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerEventDef> StarTowerEventDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<SubNoteSkillPromoteGroupDef> SubNoteSkillPromoteGroupDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharPotentialDef> CharPotentialDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<StarTowerBookFateCardBundleDef> StarTowerBookFateCardBundleDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerBookFateCardQuestDef> StarTowerBookFateCardQuestDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerBookFateCardDef> StarTowerBookFateCardDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<FateCardDef> FateCardDataTable = new DataTable<>();
|
||||
|
||||
// Infinity Tower
|
||||
|
||||
// ===== Infinity Tower =====
|
||||
@Getter private static DataTable<InfinityTowerLevelDef> InfinityTowerLevelDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<InfinityTowerDifficultyDef> InfinityTowerDifficultyDataTable = new DataTable<>();
|
||||
|
||||
// Vampire survivor
|
||||
// ===== Vampire Survivor =====
|
||||
@Getter private static DataTable<VampireSurvivorDef> VampireSurvivorDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<VampireTalentDef> VampireTalentDataTable = new DataTable<>();
|
||||
|
||||
// Score boss
|
||||
// ===== Score Boss =====
|
||||
@Getter private static DataTable<ScoreBossControlDef> ScoreBossControlDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ScoreBossRewardDef> ScoreBossRewardDataTable = new DataTable<>();
|
||||
|
||||
// ===== Misc =====
|
||||
@Getter private static DataTable<WorldClassDef> WorldClassDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GuideGroupDef> GuideGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<HandbookDef> HandbookDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<SignInDef> SignInDataTable = new DataTable<>();
|
||||
|
||||
// ===== Activity =====
|
||||
@Getter private static DataTable<ActivityDef> ActivityDataTable = new DataTable<>();
|
||||
|
||||
// Activity: Tower Defense
|
||||
@Getter private static DataTable<TowerDefenseLevelDef> TowerDefenseLevelDataTable = new DataTable<>();
|
||||
|
||||
// Activity: Trials
|
||||
@Getter private static DataTable<TrialControlDef> TrialControlDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<TrialGroupDef> TrialGroupDataTable = new DataTable<>();
|
||||
|
||||
// Activity: Levels
|
||||
@Getter private static DataTable<ActivityLevelsLevelDef> ActivityLevelsLevelDataTable = new DataTable<>();
|
||||
|
||||
// Activity: Task
|
||||
@Getter private static DataTable<ActivityTaskDef> ActivityTaskDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ActivityTaskGroupDef> ActivityTaskGroupDataTable = new DataTable<>();
|
||||
|
||||
// Activity: Shop
|
||||
@Getter private static DataTable<ActivityShopDef> ActivityShopDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ActivityShopControlDef> ActivityShopControlDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<ActivityGoodsDef> ActivityGoodsDataTable = new DataTable<>();
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.reflections.Reflections;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.achievement.AchievementHelper;
|
||||
|
||||
public class ResourceLoader {
|
||||
private static boolean loaded = false;
|
||||
@@ -22,6 +23,9 @@ public class ResourceLoader {
|
||||
// Load
|
||||
loadResources();
|
||||
|
||||
// Add hardcoded achievements params
|
||||
AchievementHelper.init();
|
||||
|
||||
// Done
|
||||
loaded = true;
|
||||
Nebula.getLogger().info("Resource loading complete");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package emu.nebula.data.resources;
|
||||
package emu.nebula.data.custom;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.resources.CharGemAttrValueDef;
|
||||
import emu.nebula.util.WeightedList;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
@@ -97,7 +98,7 @@ public class CharGemAttrGroupDef extends BaseDef {
|
||||
this.values = new WeightedList<>();
|
||||
}
|
||||
|
||||
protected void addValue(CharGemAttrValueDef value) {
|
||||
public void addValue(CharGemAttrValueDef value) {
|
||||
this.values.add(value.getRarity(), value);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import emu.nebula.game.achievement.AchievementHelper;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -18,9 +19,50 @@ public class AchievementDef extends BaseDef {
|
||||
private int Tid1;
|
||||
private int Qty1;
|
||||
|
||||
// Custom params
|
||||
private transient int param1; // -1 == any, 0 = no param, 1+ = param required
|
||||
private transient int param2; // -1 == any, 0 = no param, 1+ = param required
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public void setParams(int param1, int param2) {
|
||||
this.param1 = param1;
|
||||
this.param2 = param2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this achievement requires params to match
|
||||
*/
|
||||
public boolean hasParam1(int param) {
|
||||
if (this.param1 < 0) {
|
||||
return false;
|
||||
} else if (this.param1 == 0) {
|
||||
return param != 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this achievement requires params to match
|
||||
*/
|
||||
public boolean hasParam2(int param) {
|
||||
if (this.param2 < 0) {
|
||||
return false;
|
||||
} else if (this.param2 == 0) {
|
||||
return param != 0;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Add to cached achievement list
|
||||
var list = AchievementHelper.getCache().computeIfAbsent(this.CompleteCond, i -> new ObjectArrayList<>());
|
||||
list.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
25
src/main/java/emu/nebula/data/resources/ActivityDef.java
Normal file
25
src/main/java/emu/nebula/data/resources/ActivityDef.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "Activity.json")
|
||||
public class ActivityDef extends BaseDef {
|
||||
private int Id;
|
||||
private int ActivityType;
|
||||
|
||||
private transient emu.nebula.game.activity.ActivityType type;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.type = emu.nebula.game.activity.ActivityType.getByValue(this.ActivityType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityGoods.json", loadPriority = LoadPriority.LOW)
|
||||
public class ActivityGoodsDef extends BaseDef {
|
||||
private int Id;
|
||||
private int ShopId;
|
||||
|
||||
private int ItemId;
|
||||
private int ItemQuantity;
|
||||
private int MaximumLimit;
|
||||
private int Price;
|
||||
|
||||
private transient ItemParamMap items;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
var shop = GameData.getActivityShopDataTable().get(this.getShopId());
|
||||
if (shop != null) {
|
||||
shop.getGoods().put(this.getId(), this);
|
||||
}
|
||||
|
||||
this.items = new ItemParamMap();
|
||||
this.items.add(this.ItemId, this.ItemQuantity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.instance.InstanceData;
|
||||
import emu.nebula.game.inventory.ItemRewardList;
|
||||
import emu.nebula.game.inventory.ItemRewardParam;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityLevelsLevel.json")
|
||||
public class ActivityLevelsLevelDef extends BaseDef implements InstanceData {
|
||||
private int Id;
|
||||
private int ActivityId;
|
||||
private int EnergyConsume;
|
||||
private String CompleteRewardPreview;
|
||||
|
||||
private transient ItemRewardList firstRewards;
|
||||
private transient ItemRewardList rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNeedWorldClass() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Init reward lists
|
||||
this.firstRewards = new ItemRewardList();
|
||||
this.rewards = new ItemRewardList();
|
||||
|
||||
// Parse rewards
|
||||
var awards = JsonUtils.decodeList(this.CompleteRewardPreview, int[].class);
|
||||
if (awards == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int[] award : awards) {
|
||||
int itemId = award[0];
|
||||
int min = award[1];
|
||||
int max = award.length >= 4 ? award[2] : min;
|
||||
boolean isFirst = award[award.length - 1] == 1;
|
||||
|
||||
if (min == -1) {
|
||||
min = 0;
|
||||
max = 1;
|
||||
}
|
||||
|
||||
var reward = new ItemRewardParam(itemId, min, max);
|
||||
|
||||
if (isFirst) {
|
||||
this.firstRewards.add(reward);
|
||||
} else {
|
||||
this.rewards.add(reward);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityShopControl.json")
|
||||
public class ActivityShopControlDef extends BaseDef {
|
||||
private int Id;
|
||||
private int[] ShopIds;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
28
src/main/java/emu/nebula/data/resources/ActivityShopDef.java
Normal file
28
src/main/java/emu/nebula/data/resources/ActivityShopDef.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityShop.json")
|
||||
public class ActivityShopDef extends BaseDef {
|
||||
private int Id;
|
||||
private int CurrencyItemId;
|
||||
private int ExchangeItemId;
|
||||
private double Rate;
|
||||
|
||||
private Int2ObjectMap<ActivityGoodsDef> goods;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.goods = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
}
|
||||
40
src/main/java/emu/nebula/data/resources/ActivityTaskDef.java
Normal file
40
src/main/java/emu/nebula/data/resources/ActivityTaskDef.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityTask.json")
|
||||
public class ActivityTaskDef extends BaseDef {
|
||||
private int Id;
|
||||
private int ActivityTaskGroupId;
|
||||
|
||||
private int CompleteCond;
|
||||
private int AimNumShow;
|
||||
|
||||
private int Tid1;
|
||||
private int Qty1;
|
||||
private int Tid2;
|
||||
private int Qty2;
|
||||
|
||||
private transient ItemParamMap rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.rewards = new ItemParamMap();
|
||||
|
||||
if (this.Tid1 > 0) {
|
||||
this.rewards.add(this.Tid1, this.Qty1);
|
||||
}
|
||||
if (this.Tid2 > 0) {
|
||||
this.rewards.add(this.Tid2, this.Qty2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ActivityTaskGroup.json")
|
||||
public class ActivityTaskGroupDef extends BaseDef {
|
||||
private int Id;
|
||||
private int ActivityId;
|
||||
|
||||
private int Reward1;
|
||||
private int RewardQty1;
|
||||
private int Reward2;
|
||||
private int RewardQty2;
|
||||
private int Reward3;
|
||||
private int RewardQty3;
|
||||
private int Reward4;
|
||||
private int RewardQty4;
|
||||
private int Reward5;
|
||||
private int RewardQty5;
|
||||
private int Reward6;
|
||||
private int RewardQty6;
|
||||
|
||||
private transient ItemParamMap rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.rewards = new ItemParamMap();
|
||||
|
||||
if (this.Reward1 > 0) {
|
||||
this.rewards.add(this.Reward1, this.RewardQty1);
|
||||
}
|
||||
if (this.Reward2 > 0) {
|
||||
this.rewards.add(this.Reward2, this.RewardQty2);
|
||||
}
|
||||
if (this.Reward3 > 0) {
|
||||
this.rewards.add(this.Reward3, this.RewardQty3);
|
||||
}
|
||||
if (this.Reward4 > 0) {
|
||||
this.rewards.add(this.Reward4, this.RewardQty4);
|
||||
}
|
||||
if (this.Reward5 > 0) {
|
||||
this.rewards.add(this.Reward5, this.RewardQty5);
|
||||
}
|
||||
if (this.Reward6 > 0) {
|
||||
this.rewards.add(this.Reward6, this.RewardQty6);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package emu.nebula.data.resources;
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.custom.CharGemAttrGroupDef;
|
||||
import emu.nebula.util.CustomIntArray;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.util.WeightedList;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharPotential.json")
|
||||
public class CharPotentialDef extends BaseDef {
|
||||
private int Id;
|
||||
|
||||
private int[] MasterSpecificPotentialIds;
|
||||
private int[] AssistSpecificPotentialIds;
|
||||
private int[] CommonPotentialIds;
|
||||
private int[] MasterNormalPotentialIds;
|
||||
private int[] AssistNormalPotentialIds;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
@@ -61,5 +62,8 @@ public class CharacterDef extends BaseDef {
|
||||
public void onLoad() {
|
||||
this.elementType = ElementType.getByValue(this.EET);
|
||||
this.chats = new ObjectArrayList<>();
|
||||
|
||||
// Sort gem slots
|
||||
this.GemSlots = Arrays.stream(this.GemSlots).sorted().toArray();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ public class DiscDef extends BaseDef {
|
||||
private int TransformItemId;
|
||||
private int[] MaxStarTransformItem;
|
||||
private int[] ReadReward;
|
||||
|
||||
private int SecondarySkillGroupId1;
|
||||
private int SecondarySkillGroupId2;
|
||||
private int SubNoteSkillGroupId;
|
||||
|
||||
private transient ElementType elementType;
|
||||
|
||||
40
src/main/java/emu/nebula/data/resources/DropPkgDef.java
Normal file
40
src/main/java/emu/nebula/data/resources/DropPkgDef.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DropPkg.json")
|
||||
public class DropPkgDef extends BaseDef {
|
||||
private int PkgId;
|
||||
private int ItemId;
|
||||
|
||||
private static Int2ObjectMap<IntList> PACKAGES = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return PkgId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
var packageList = PACKAGES.computeIfAbsent(this.PkgId, i -> new IntArrayList());
|
||||
packageList.add(this.ItemId);
|
||||
}
|
||||
|
||||
public static int getRandomDrop(int packageId) {
|
||||
var packageList = PACKAGES.get(packageId);
|
||||
|
||||
if (packageList == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Utils.randomElement(packageList);
|
||||
}
|
||||
}
|
||||
34
src/main/java/emu/nebula/data/resources/EventOptionsDef.java
Normal file
34
src/main/java/emu/nebula/data/resources/EventOptionsDef.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* We don't need a DataTable for this, since we are only using this class to verify event options for the client
|
||||
*/
|
||||
@Getter
|
||||
@ResourceType(name = "EventOptions.json", loadPriority = LoadPriority.LOW)
|
||||
public class EventOptionsDef extends BaseDef {
|
||||
private int Id;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Get event
|
||||
var event = GameData.getStarTowerEventDataTable().get(this.Id / 100);
|
||||
if (event == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to avaliable options
|
||||
event.getOptionIds().add(this.getId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "InfinityTowerDifficulty.json")
|
||||
public class InfinityTowerDifficultyDef extends BaseDef {
|
||||
private int Id;
|
||||
private int TowerId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.inventory.ItemRewardParam;
|
||||
@@ -25,8 +26,13 @@ public class InfinityTowerLevelDef extends BaseDef {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getEnergyConsume() {
|
||||
return 0;
|
||||
public int getTowerId() {
|
||||
var diff = GameData.getInfinityTowerDifficultyDataTable().get(this.DifficultyId);
|
||||
if (diff == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return diff.getTowerId();
|
||||
}
|
||||
|
||||
public ItemParamMap generateRewards() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -10,11 +11,48 @@ public class PotentialDef extends BaseDef {
|
||||
private int Id;
|
||||
private int CharId;
|
||||
private int Build;
|
||||
private int BranchType;
|
||||
private int MaxLevel;
|
||||
private int[] BuildScore;
|
||||
|
||||
private String BriefDesc;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public boolean isRare() {
|
||||
return this.BranchType != 3;
|
||||
}
|
||||
|
||||
public int getMaxLevel() {
|
||||
// Check if regular potential
|
||||
if (this.BranchType == 3) {
|
||||
return this.BuildScore.length;
|
||||
}
|
||||
|
||||
// Special potential should always have a max level of 1
|
||||
return this.MaxLevel;
|
||||
}
|
||||
|
||||
public int getMaxLevel(StarTowerGame game) {
|
||||
// Check if regular potential
|
||||
if (this.BranchType == 3) {
|
||||
return this.MaxLevel + game.getModifiers().getExtraMaxPotentialLevel();
|
||||
}
|
||||
|
||||
// Special potential should always have a max level of 1
|
||||
return this.MaxLevel;
|
||||
}
|
||||
|
||||
public int getBuildScore(int level) {
|
||||
int index = level - 1;
|
||||
|
||||
if (index >= this.BuildScore.length) {
|
||||
index = this.BuildScore.length - 1;
|
||||
}
|
||||
|
||||
return this.BuildScore[index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "ScoreBossReward.json")
|
||||
public class ScoreBossRewardDef extends BaseDef {
|
||||
private int StarNeed;
|
||||
private int RewardItemId1;
|
||||
private int RewardNum1;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return StarNeed;
|
||||
}
|
||||
}
|
||||
118
src/main/java/emu/nebula/data/resources/SecondarySkillDef.java
Normal file
118
src/main/java/emu/nebula/data/resources/SecondarySkillDef.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "SecondarySkill.json")
|
||||
public class SecondarySkillDef extends BaseDef {
|
||||
private int Id;
|
||||
private int GroupId;
|
||||
private int Score;
|
||||
private String NeedSubNoteSkills;
|
||||
|
||||
private transient ItemParamMap reqSubNotes;
|
||||
|
||||
@Getter
|
||||
private static transient Int2ObjectMap<List<SecondarySkillDef>> groups = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public boolean match(ItemParamMap subNotes) {
|
||||
for (var item : this.reqSubNotes) {
|
||||
int reqId = item.getIntKey();
|
||||
int reqCount = item.getIntValue();
|
||||
|
||||
int curCount = subNotes.get(reqId);
|
||||
if (curCount < reqCount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Setup required subnotes
|
||||
this.reqSubNotes = ItemParamMap.fromJsonString(this.NeedSubNoteSkills);
|
||||
|
||||
// Add to group cache
|
||||
var group = groups.computeIfAbsent(this.GroupId, id -> new ArrayList<>());
|
||||
group.add(this);
|
||||
|
||||
// Clear to save memory
|
||||
this.NeedSubNoteSkills = null;
|
||||
}
|
||||
|
||||
// Static sub note skill group group
|
||||
|
||||
public static List<SecondarySkillDef> getGroup(int id) {
|
||||
return groups.get(id);
|
||||
}
|
||||
|
||||
public static IntSet calculateSecondarySkills(int[] discIds, ItemParamMap subNotes) {
|
||||
var secondarySkills = new IntOpenHashSet();
|
||||
|
||||
// Get first 3 discs
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Disc id
|
||||
int discId = discIds[i];
|
||||
|
||||
// Get disc data
|
||||
var data = GameData.getDiscDataTable().get(discId);
|
||||
if (data == null) continue;
|
||||
|
||||
// Add secondary skills
|
||||
int s1= getSecondarySkill(subNotes, data.getSecondarySkillGroupId1());
|
||||
if (s1 > 0) {
|
||||
secondarySkills.add(s1);
|
||||
}
|
||||
|
||||
int s2 = getSecondarySkill(subNotes, data.getSecondarySkillGroupId2());
|
||||
if (s2 > 0) {
|
||||
secondarySkills.add(s2);
|
||||
}
|
||||
}
|
||||
|
||||
return secondarySkills;
|
||||
}
|
||||
|
||||
private static int getSecondarySkill(ItemParamMap subNotes, int groupId) {
|
||||
// Check group id
|
||||
if (groupId <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get group
|
||||
var group = SecondarySkillDef.getGroup(groupId);
|
||||
if (group == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reverse iterator to try and match highest secondary skill first
|
||||
for (int i = group.size() - 1; i >= 0; i--) {
|
||||
var data = group.get(i);
|
||||
|
||||
if (data.match(subNotes)) {
|
||||
return data.getId();
|
||||
}
|
||||
}
|
||||
|
||||
// Failure
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
20
src/main/java/emu/nebula/data/resources/SignInDef.java
Normal file
20
src/main/java/emu/nebula/data/resources/SignInDef.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "SignIn.json")
|
||||
public class SignInDef extends BaseDef {
|
||||
private int Group;
|
||||
private int Day;
|
||||
private int ItemId;
|
||||
private int ItemQty;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return (this.Group << 16) + this.Day;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import lombok.Getter;
|
||||
@ResourceType(name = "StarTower.json")
|
||||
public class StarTowerDef extends BaseDef {
|
||||
private int Id;
|
||||
private int GroupId;
|
||||
private int Difficulty;
|
||||
private int[] FloorNum;
|
||||
|
||||
private transient int maxFloors;
|
||||
@@ -19,8 +21,8 @@ public class StarTowerDef extends BaseDef {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getMaxFloor(int stage) {
|
||||
int index = stage - 1;
|
||||
public int getMaxFloor(int stageNum) {
|
||||
int index = stageNum - 1;
|
||||
|
||||
if (index < 0 || index >= this.FloorNum.length) {
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "StarTowerEvent.json")
|
||||
public class StarTowerEventDef extends BaseDef {
|
||||
private int Id;
|
||||
private int[] RelatedNPCs;
|
||||
|
||||
private transient IntList optionIds;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy of our option ids
|
||||
*/
|
||||
public IntList getClonedOptionIds() {
|
||||
var list = new IntArrayList();
|
||||
list.addAll(this.getOptionIds());
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.optionIds = new IntArrayList();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.tower.room.RoomType;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -10,11 +13,19 @@ public class StarTowerStageDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Stage;
|
||||
private int Floor;
|
||||
private int RoomType;
|
||||
private int InteriorCurrencyQuantity;
|
||||
@SerializedName("RoomType")
|
||||
private int RoomTypeValue;
|
||||
|
||||
private transient RoomType roomType;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.roomType = RoomType.getByValue(this.RoomTypeValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "StoryEvidence.json")
|
||||
public class StoryEvidenceDef extends BaseDef {
|
||||
private int Id;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "TowerDefenseLevel.json")
|
||||
public class TowerDefenseLevelDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Condition2;
|
||||
private int Condition3;
|
||||
private int Item1;
|
||||
private int Qty1;
|
||||
private int Item2;
|
||||
private int Qty2;
|
||||
|
||||
private transient ItemParamMap rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Parse rewards
|
||||
this.rewards = new ItemParamMap();
|
||||
this.rewards.add(this.Item1, this.Qty1);
|
||||
this.rewards.add(this.Item2, this.Qty2);
|
||||
}
|
||||
}
|
||||
18
src/main/java/emu/nebula/data/resources/TrialControlDef.java
Normal file
18
src/main/java/emu/nebula/data/resources/TrialControlDef.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "TrialControl.json")
|
||||
public class TrialControlDef extends BaseDef {
|
||||
private int Id;
|
||||
private IntOpenHashSet GroupIds;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
41
src/main/java/emu/nebula/data/resources/TrialGroupDef.java
Normal file
41
src/main/java/emu/nebula/data/resources/TrialGroupDef.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "TrialGroup.json")
|
||||
public class TrialGroupDef extends BaseDef {
|
||||
private int Id;
|
||||
|
||||
private int RewardId1;
|
||||
private int Qty1;
|
||||
private int RewardId2;
|
||||
private int Qty2;
|
||||
private int RewardId3;
|
||||
private int Qty3;
|
||||
|
||||
private transient ItemParamMap rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.rewards = new ItemParamMap();
|
||||
|
||||
if (this.RewardId1 > 0) {
|
||||
this.rewards.add(this.RewardId1, this.Qty1);
|
||||
}
|
||||
if (this.RewardId2 > 0) {
|
||||
this.rewards.add(this.RewardId2, this.Qty2);
|
||||
}
|
||||
if (this.RewardId3 > 0) {
|
||||
this.rewards.add(this.RewardId3, this.Qty3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.game.instance.InstanceData;
|
||||
import emu.nebula.game.inventory.ItemRewardList;
|
||||
import emu.nebula.game.inventory.ItemRewardParam;
|
||||
@@ -10,9 +12,10 @@ import emu.nebula.util.JsonUtils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "WeekBossLevel.json")
|
||||
@ResourceType(name = "WeekBossLevel.json", loadPriority = LoadPriority.LOW)
|
||||
public class WeekBossLevelDef extends BaseDef implements InstanceData {
|
||||
private int Id;
|
||||
private int Difficulty;
|
||||
private int PreLevelId;
|
||||
private int NeedWorldClass;
|
||||
private String BaseAwardPreview;
|
||||
@@ -44,14 +47,33 @@ public class WeekBossLevelDef extends BaseDef implements InstanceData {
|
||||
for (int[] award : awards) {
|
||||
int itemId = award[0];
|
||||
int min = award[1];
|
||||
int max = award.length >= 4 ? award[2] : min;
|
||||
int max = award[1];
|
||||
boolean isFirst = award[award.length - 1] == 1;
|
||||
|
||||
// Set reward count based on difficulty
|
||||
if (min == -1) {
|
||||
min = 0;
|
||||
max = 1;
|
||||
min = this.Difficulty;
|
||||
max = this.Difficulty;
|
||||
|
||||
var item = GameData.getItemDataTable().get(itemId);
|
||||
if (item != null) {
|
||||
switch (item.getRarity()) {
|
||||
case 2:
|
||||
max = this.Difficulty * 3;
|
||||
break;
|
||||
case 3:
|
||||
min = this.Difficulty * 2;
|
||||
max = this.Difficulty * 6;
|
||||
break;
|
||||
case 4:
|
||||
min = this.Difficulty * 3;
|
||||
max = this.Difficulty * 9;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create reward param
|
||||
var reward = new ItemRewardParam(itemId, min, max);
|
||||
|
||||
if (isFirst) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import de.bwaldvogel.mongo.backend.h2.H2Backend;
|
||||
import de.bwaldvogel.mongo.backend.memory.MemoryBackend;
|
||||
import dev.morphia.*;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.mapping.Mapper;
|
||||
import dev.morphia.mapping.MapperOptions;
|
||||
import dev.morphia.query.FindOptions;
|
||||
import dev.morphia.query.Sort;
|
||||
@@ -84,7 +83,7 @@ public final class DatabaseManager {
|
||||
.stream()
|
||||
.filter(cls -> {
|
||||
Entity e = cls.getAnnotation(Entity.class);
|
||||
return e != null && !e.value().equals(Mapper.IGNORED_FIELDNAME);
|
||||
return e != null;
|
||||
})
|
||||
.toList();
|
||||
|
||||
@@ -164,12 +163,16 @@ public final class DatabaseManager {
|
||||
return getDatastore().find(cls).stream();
|
||||
}
|
||||
|
||||
public <T> List<T> getSortedObjects(Class<T> cls, String filter, int limit) {
|
||||
public <T> List<T> getSortedObjects(Class<T> cls, String filter, int value, String sortBy, int limit) {
|
||||
var options = new FindOptions()
|
||||
.sort(Sort.descending(filter))
|
||||
.sort(Sort.descending(sortBy))
|
||||
.limit(limit);
|
||||
|
||||
return getDatastore().find(cls).iterator(options).toList();
|
||||
return getDatastore()
|
||||
.find(cls)
|
||||
.filter(Filters.eq(filter, value))
|
||||
.iterator(options)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public <T> void save(T obj) {
|
||||
|
||||
@@ -8,6 +8,8 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.activity.ActivityModule;
|
||||
import emu.nebula.game.ban.BanModule;
|
||||
import emu.nebula.game.gacha.GachaModule;
|
||||
import emu.nebula.game.player.PlayerModule;
|
||||
import emu.nebula.game.scoreboss.ScoreBossModule;
|
||||
@@ -27,7 +29,9 @@ public class GameContext implements Runnable {
|
||||
private final PlayerModule playerModule;
|
||||
private final GachaModule gachaModule;
|
||||
private final TutorialModule tutorialModule;
|
||||
private final ActivityModule activityModule;
|
||||
private final ScoreBossModule scoreBossModule;
|
||||
private final BanModule banModule;
|
||||
|
||||
// Game loop
|
||||
private final ScheduledExecutorService scheduler;
|
||||
@@ -35,6 +39,7 @@ public class GameContext implements Runnable {
|
||||
// Daily
|
||||
private long epochDays;
|
||||
private int epochWeeks;
|
||||
private int epochMonths;
|
||||
|
||||
public GameContext() {
|
||||
this.sessions = new Object2ObjectOpenHashMap<>();
|
||||
@@ -43,7 +48,9 @@ public class GameContext implements Runnable {
|
||||
this.playerModule = new PlayerModule(this);
|
||||
this.gachaModule = new GachaModule(this);
|
||||
this.tutorialModule = new TutorialModule(this);
|
||||
this.activityModule = new ActivityModule(this);
|
||||
this.scoreBossModule = new ScoreBossModule(this);
|
||||
this.banModule = new BanModule(this);
|
||||
|
||||
// Run game loop
|
||||
this.scheduler = Executors.newScheduledThreadPool(1);
|
||||
@@ -102,12 +109,17 @@ public class GameContext implements Runnable {
|
||||
var instant = Instant.now().plusSeconds(offset);
|
||||
var date = LocalDate.ofInstant(instant, GameConstants.UTC_ZONE);
|
||||
|
||||
// Update epoch days
|
||||
long lastEpochDays = this.epochDays;
|
||||
this.epochDays = date.toEpochDay();
|
||||
this.epochWeeks = Utils.getWeeks(this.epochDays);
|
||||
|
||||
// Check if the day was changed
|
||||
if (this.epochDays > lastEpochDays) {
|
||||
// Update epoch weeks/months
|
||||
this.epochWeeks = Utils.getWeeks(this.epochDays);
|
||||
this.epochMonths = Utils.getMonths(this.epochDays);
|
||||
|
||||
// Reset dailies for players
|
||||
this.resetDailies();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package emu.nebula.game.achievement;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum AchievementCondition {
|
||||
AchievementSpecific (1),
|
||||
AchievementTotal (2),
|
||||
BattleTotal (3),
|
||||
CharacterAcquire (5),
|
||||
CharacterAcquireQuantityRarityAndAdvancement (6),
|
||||
CharacterAdvanceTotal (7),
|
||||
CharacterSkillUpTotal (8),
|
||||
CharacterSkillWithSpecificUpTotal (9),
|
||||
CharacterSpecific (10),
|
||||
CharacterUpLevel (11),
|
||||
CharacterUpTotal (12),
|
||||
CharacterWithSpecificAdvance (13),
|
||||
CharacterWithSpecificAffinity (14),
|
||||
CharacterWithSpecificUpLevel (15),
|
||||
CharactersWithSpecificLevelAndQuantity (16),
|
||||
CharactersWithSpecificNumberLevelAndAttributes (17),
|
||||
CharactersWithSpecificPlot (18),
|
||||
CharactersWithSpecificQuantityAdvancementCountAndAttribute (19),
|
||||
CharactersWithSpecificQuantityAndRarity (20),
|
||||
CharactersWithSpecificQuantityRarityAndAdvancement (21),
|
||||
CharactersWithSpecificQuantityRarityAndLevel (22),
|
||||
ChatTotal (23),
|
||||
DailyInstanceClearSpecificDifficultyAndTotal (24),
|
||||
DailyInstanceClearSpecificTypeAndTotal (25),
|
||||
DailyInstanceClearTotal (26),
|
||||
DateSpecific (27),
|
||||
DiscAcquire (28),
|
||||
DiscAcquireSpecificQuantityAndRarity (29),
|
||||
DiscAcquireQuantityLevelAndRarity (30),
|
||||
DiscAcquireQuantityPhaseAndRarity (31),
|
||||
DiscAcquireQuantityStarAndRarity (32),
|
||||
DiscLimitBreakTotal (33),
|
||||
DiscPromoteTotal (34),
|
||||
DiscStrengthenTotal (35),
|
||||
DiscWithSpecificQuantityLevelAndRarity (36),
|
||||
DiscWithSpecificQuantityPhaseAndRarity (37),
|
||||
DiscWithSpecificQuantityStarAndRarity (38),
|
||||
GachaCharacterNotSSRTotal (40),
|
||||
GachaCharacterTenModeSSRTotal (41),
|
||||
GachaCharacterTotal (42),
|
||||
GachaTenModeAcquireQuantityAndRarityItems (43),
|
||||
GachaTotal (44),
|
||||
GiftGiveTotal (45),
|
||||
InfinityTowerClearSpecificFloor (46),
|
||||
InfinityTowerClearTotal (47),
|
||||
ItemsAdd (48),
|
||||
ItemsDeplete (49),
|
||||
ItemsProductTotal (50),
|
||||
LoginTotal (51),
|
||||
QuestTravelerDuelChallengeTotal (52),
|
||||
QuestTourGuideSpecific (53),
|
||||
QuestTravelerDuelSpecific (54),
|
||||
QuestWithSpecificType (55),
|
||||
RegionBossClearSpecificFullStarWithBossIdAndDifficulty (56),
|
||||
RegionBossClearSpecificLevelWithDifficultyAndTotal (57),
|
||||
RegionBossClearSpecificTotal (58),
|
||||
RegionBossClearTotal (59),
|
||||
SkillsWithSpecificQuantityAndLevel (60),
|
||||
SkinAcquire (61),
|
||||
StageClearSpecificStars (62),
|
||||
StoryClear (63),
|
||||
TravelerDuelChallengeSpecificBoosLevelWithDifficultyAndTotal (64),
|
||||
TravelerDuelClearBossTotal (65),
|
||||
TravelerDuelClearSpecificBossIdAndDifficulty (66),
|
||||
TravelerDuelChallengeClearSpecificBossLevelAndAffix (67),
|
||||
TravelerDuelClearSpecificBossLevelWithDifficultyAndTotal (68),
|
||||
TravelerDuelClearSpecificBossTotal (69),
|
||||
TravelerDuelChallengeRankUploadTotal (70),
|
||||
WorldClassSpecific (71),
|
||||
RegionBossClearSpecificTypeWithTotal (72),
|
||||
CharactersWithSpecificDatingCount (73),
|
||||
CharactersDatingTotal (74),
|
||||
VampireSurvivorScoreTotal (75),
|
||||
VampireSurvivorSpecificLevelWithSpecificScore (76),
|
||||
VampireSurvivorPassedSpecificLevel (77),
|
||||
CharacterParticipateTowerNumber (78),
|
||||
CharacterAllSkillReachSpecificLevel (79),
|
||||
TravelerDuelPlayTotal (80),
|
||||
VampireClearTotal (81),
|
||||
VampireWithSpecificClearTotal (82),
|
||||
AgentFinishTotal (83),
|
||||
AgentWithSpecificFinishTotal (84),
|
||||
ActivityMiningEnterLayer (86),
|
||||
ActivityMiningDestroyGrid (87),
|
||||
BossRushTotalStars (88),
|
||||
InfinityTowerClearSpecificDifficultyAndTotal (89),
|
||||
SkillInstanceClearTotal (90),
|
||||
VampireSurvivorSpecificPassedLevel (91),
|
||||
WeekBoosClearSpecificDifficultyAndTotal (92),
|
||||
NpcAffinityWithSpecificLevel (93),
|
||||
CharacterPassedWithSpecificTowerAndCount (94),
|
||||
JointDrillScoreTotal (95),
|
||||
CharGemInstanceClearTotal (104),
|
||||
DailyShopReceiveShopTotal (105),
|
||||
AgentApplyTotal (106),
|
||||
DiscSpecific (114),
|
||||
ClientReport (200),
|
||||
TowerBattleTimes (501),
|
||||
TowerBossChallengeSpecificHighRewardWithTotal (502),
|
||||
TowerBuildSpecificCharacter (503),
|
||||
TowerBuildSpecificScoreWithTotal (504),
|
||||
TowerClearSpecificCharacterTypeWithTotal (505),
|
||||
TowerClearSpecificGroupIdAndDifficulty (506),
|
||||
TowerClearSpecificLevelWithDifficultyAndTotal (507),
|
||||
TowerClearTotal (508),
|
||||
TowerEnterRoom (509),
|
||||
TowerEventTimes (511),
|
||||
TowerFateTimes (512),
|
||||
TowerItemsGet (513),
|
||||
TowerSpecificDifficultyShopBuyTimes (514),
|
||||
TowerGrowthSpecificNote (515),
|
||||
TowerClearSpecificLevelWithDifficultyAndTotalHistory (516),
|
||||
TowerBookWithSpecificEvent (517),
|
||||
TowerBookWithSpecificFateCard (518),
|
||||
TowerBookWithSpecificPotential (520),
|
||||
TowerBuildSpecificDifficultyAndScoreWithTotal (521),
|
||||
TowerSpecificDifficultyStrengthenMachineTotal (522),
|
||||
TowerSpecificDifficultyKillBossTotal (524),
|
||||
TowerBookSpecificCharWithPotentialTotal (525),
|
||||
TowerBuildSpecificCharSpecificScoreWithTotal (526),
|
||||
TowerGrowthWithSpecificNote (527),
|
||||
TowerSpecificFateCardReRollTotal (528),
|
||||
TowerSpecificPotentialReRollTotal (529),
|
||||
TowerSpecificShopReRollTotal (530),
|
||||
TowerSpecificNoteActivateTotal (531),
|
||||
TowerSpecificNoteLevelTotal (532),
|
||||
TowerSpecificPotentialBonusTotal (533),
|
||||
TowerSpecificPotentialLuckyTotal (534),
|
||||
TowerSpecificShopBuyDiscountTotal (535),
|
||||
TowerSpecificSecondarySkillActivateTotal (536),
|
||||
TowerSpecificGetExtraNoteLvTotal (537),
|
||||
TowerSweepTimes (539),
|
||||
TowerSweepTotal (540);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<AchievementCondition> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (AchievementCondition type : AchievementCondition.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private AchievementCondition(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static AchievementCondition getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
166
src/main/java/emu/nebula/game/achievement/AchievementHelper.java
Normal file
166
src/main/java/emu/nebula/game/achievement/AchievementHelper.java
Normal file
@@ -0,0 +1,166 @@
|
||||
package emu.nebula.game.achievement;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.AchievementDef;
|
||||
import emu.nebula.game.tower.room.RoomType;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
|
||||
// Because achievements in the data files do not have params, we will hardcode them here
|
||||
public class AchievementHelper {
|
||||
// Cache
|
||||
private static IntSet incrementalAchievementSet = new IntOpenHashSet();
|
||||
|
||||
@Getter
|
||||
private static Int2ObjectMap<List<AchievementDef>> cache = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static List<AchievementDef> getAchievementsByCondition(int condition) {
|
||||
return cache.get(condition);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public static boolean isIncrementalAchievement(int condition) {
|
||||
return incrementalAchievementSet.contains(condition);
|
||||
}
|
||||
|
||||
// Fix params
|
||||
|
||||
public static void init() {
|
||||
// Cache total achievements
|
||||
for (var condition : AchievementCondition.values()) {
|
||||
if (condition.name().endsWith("Total") || condition.name().endsWith("Times")) {
|
||||
incrementalAchievementSet.add(condition.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
incrementalAchievementSet.remove(AchievementCondition.AchievementTotal.getValue());
|
||||
|
||||
incrementalAchievementSet.add(AchievementCondition.ItemsAdd.getValue());
|
||||
incrementalAchievementSet.add(AchievementCondition.ItemsDeplete.getValue());
|
||||
|
||||
incrementalAchievementSet.add(AchievementCondition.TowerItemsGet.getValue());
|
||||
incrementalAchievementSet.add(AchievementCondition.TowerEnterRoom.getValue());
|
||||
|
||||
// Fix params
|
||||
fixParams();
|
||||
}
|
||||
|
||||
private static void fixParams() {
|
||||
// Clear "Misstep On One"
|
||||
addParam(56, 401, 0); // Custom trigger
|
||||
|
||||
// Clear "Currents and Shadows"
|
||||
addParam(57, 102, 0);
|
||||
addParam(58, 103, 0);
|
||||
addParam(59, 104, 0);
|
||||
addParam(60, 105, 0);
|
||||
addParam(61, 106, 0);
|
||||
addParam(62, 107, 0);
|
||||
addParam(63, 108, 0);
|
||||
|
||||
// Clear "Dust and Flames"
|
||||
addParam(64, 202, 0);
|
||||
addParam(65, 203, 0);
|
||||
addParam(66, 204, 0);
|
||||
addParam(67, 205, 0);
|
||||
addParam(68, 206, 0);
|
||||
addParam(69, 207, 0);
|
||||
addParam(70, 208, 0);
|
||||
|
||||
// Clear "Storm and Thunder"
|
||||
addParam(71, 302, 0);
|
||||
addParam(72, 303, 0);
|
||||
addParam(73, 304, 0);
|
||||
addParam(74, 305, 0);
|
||||
addParam(75, 306, 0);
|
||||
addParam(76, 307, 0);
|
||||
addParam(77, 308, 0);
|
||||
|
||||
// First Ascension
|
||||
addParam(498, 0, 1);
|
||||
|
||||
// Monolith Conqueror
|
||||
addParam(78, 2, 0);
|
||||
addParam(79, 4, 0);
|
||||
addParam(80, 6, 0);
|
||||
addParam(81, 7, 0);
|
||||
|
||||
// Money
|
||||
addParam(25, GameConstants.GOLD_ITEM_ID, 0);
|
||||
addParam(26, GameConstants.GOLD_ITEM_ID, 0);
|
||||
addParam(27, GameConstants.GOLD_ITEM_ID, 0);
|
||||
addParam(28, GameConstants.GOLD_ITEM_ID, 0);
|
||||
addParam(29, GameConstants.GOLD_ITEM_ID, 0);
|
||||
|
||||
// Ininfite tower
|
||||
for (int diff = 10, id = 270; diff <= 60; diff += 10) {
|
||||
addParam(id++, 11000 + diff, 0); // Infinite Arena
|
||||
addParam(id++, 51000 + diff, 0); // Shake the Floor
|
||||
addParam(id++, 41000 + diff, 0); // Elegance and Flow
|
||||
addParam(id++, 71000 + diff, 0); // Upbeat Party
|
||||
addParam(id++, 31000 + diff, 0); // Thrilling Beat
|
||||
addParam(id++, 21000 + diff, 0); // Flames and Beats
|
||||
addParam(id++, 61000 + diff, 0); // Sinister Ritual
|
||||
}
|
||||
|
||||
// Character count
|
||||
addParams(393, 398, 1, 0);
|
||||
|
||||
// Disc count
|
||||
addParams(382, 387, 1, 0);
|
||||
|
||||
// Star Tower team clear
|
||||
addParams(95, 98, 1, 0); // Aqua team clear
|
||||
addParams(99, 102, 2, 0); // Fire team clear
|
||||
addParams(103, 106, 3, 0); // Earth team clear
|
||||
addParams(107, 110, 4, 0); // Wind team clear
|
||||
addParams(111, 114, 5, 0); // Light team clear
|
||||
addParams(115, 118, 6, 0); // Dark team clear
|
||||
|
||||
// Star tower items
|
||||
addParams(139, 144, GameConstants.TOWER_COIN_ITEM_ID, 0);
|
||||
|
||||
addParams(145, 149, 90011, 0);
|
||||
addParams(150, 154, 90012, 0);
|
||||
addParams(155, 159, 90013, 0);
|
||||
addParams(160, 164, 90014, 0);
|
||||
addParams(165, 169, 90015, 0);
|
||||
addParams(170, 174, 90016, 0);
|
||||
addParams(175, 179, 90017, 0);
|
||||
|
||||
addParams(180, 184, 90018, 0);
|
||||
addParams(185, 189, 90019, 0);
|
||||
addParams(190, 194, 90020, 0);
|
||||
addParams(195, 199, 90021, 0);
|
||||
addParams(200, 204, 90022, 0);
|
||||
addParams(205, 209, 90023, 0);
|
||||
|
||||
// Star tower rooms
|
||||
addParams(210, 216, RoomType.BattleRoom.getValue() + 1, 0);
|
||||
addParams(217, 223, RoomType.EliteBattleRoom.getValue() + 1, 0);
|
||||
addParams(224, 230, RoomType.BossRoom.getValue() + 1, 0);
|
||||
addParams(231, 237, RoomType.FinalBossRoom.getValue() + 1, 0);
|
||||
addParams(238, 244, RoomType.ShopRoom.getValue() + 1, 0);
|
||||
addParams(245, 251, RoomType.EventRoom.getValue() + 1, 0);
|
||||
}
|
||||
|
||||
private static void addParam(int achievementId, int param1, int param2) {
|
||||
var data = GameData.getAchievementDataTable().get(achievementId);
|
||||
if (data == null) return;
|
||||
|
||||
data.setParams(param1, param2);
|
||||
}
|
||||
|
||||
private static void addParams(int start, int end, int param1, int param2) {
|
||||
for (int id = start; id <= end; id++) {
|
||||
addParam(id, param1, param2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
package emu.nebula.game.achievement;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.AchievementDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.Achievements;
|
||||
import emu.nebula.proto.Public.Events;
|
||||
|
||||
import lombok.Getter;
|
||||
import us.hebi.quickbuf.RepeatedInt;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "achievements", useDiscriminator = false)
|
||||
public class AchievementManager extends PlayerManager implements GameDatabaseObject {
|
||||
@Id
|
||||
private int uid;
|
||||
|
||||
// Achievement data
|
||||
private Map<Integer, GameAchievement> achievements;
|
||||
|
||||
// Flags
|
||||
private transient boolean queueSave;
|
||||
private transient boolean hasCompleted;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public AchievementManager() {
|
||||
|
||||
}
|
||||
|
||||
public AchievementManager(Player player) {
|
||||
super(player);
|
||||
this.uid = player.getUid();
|
||||
this.achievements = new HashMap<>();
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public synchronized int getCompletedAchievementsCount() {
|
||||
return (int) this.getAchievements().values().stream()
|
||||
.filter(GameAchievement::isComplete)
|
||||
.count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any unclaimed achievements
|
||||
*/
|
||||
public synchronized boolean hasNewAchievements() {
|
||||
for (var achievement : this.getAchievements().values()) {
|
||||
if (achievement.isClaimable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized GameAchievement getAchievement(AchievementDef data) {
|
||||
// Try and get achievement normally
|
||||
var achievement = this.getAchievements().get(data.getId());
|
||||
|
||||
// Create achievement if it doesnt exist
|
||||
if (achievement == null) {
|
||||
achievement = new GameAchievement(data);
|
||||
|
||||
this.getAchievements().put(achievement.getId(), achievement);
|
||||
}
|
||||
|
||||
return achievement;
|
||||
}
|
||||
|
||||
public synchronized void handleClientEvents(Events events) {
|
||||
// Parse events
|
||||
for (var event : events.getList()) {
|
||||
// Check id
|
||||
if (event.getId() != AchievementCondition.ClientReport.getValue()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (event.getData().length() < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get achievement id and progress
|
||||
int id = event.getData().get(1);
|
||||
int progress = event.getData().get(0);
|
||||
|
||||
if (progress <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get achievement data
|
||||
var data = GameData.getAchievementDataTable().get(id);
|
||||
if (data == null) continue;
|
||||
|
||||
// Make sure achivement can be completed by the client
|
||||
if (data.getCompleteCond() != AchievementCondition.ClientReport.getValue()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get achievement
|
||||
var achievement = this.getAchievement(data);
|
||||
|
||||
// Update achievement
|
||||
boolean changed = achievement.trigger(true, progress, 0, 0);
|
||||
|
||||
// Sync with client if achievement was changed
|
||||
if (changed) {
|
||||
this.syncAchievement(achievement);
|
||||
}
|
||||
}
|
||||
|
||||
// Update total achievements
|
||||
if (this.hasCompleted) {
|
||||
this.hasCompleted = false;
|
||||
this.getPlayer().trigger(AchievementCondition.AchievementTotal, this.getCompletedAchievementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void trigger(AchievementCondition condition, int progress) {
|
||||
this.trigger(condition.getValue(), progress, 0, 0);
|
||||
}
|
||||
|
||||
public synchronized void trigger(AchievementCondition condition, int progress, int param1, int param2) {
|
||||
this.trigger(condition.getValue(), progress, param1, param2);
|
||||
}
|
||||
|
||||
public synchronized void trigger(int condition, int progress, int param1, int param2) {
|
||||
// Sanity check
|
||||
if (progress <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Blacklist
|
||||
if (condition == AchievementCondition.ClientReport.getValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get achievements to trigger
|
||||
var triggerList = AchievementHelper.getAchievementsByCondition(condition);
|
||||
|
||||
if (triggerList == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check what type of achievement condition this is
|
||||
boolean isTotal = AchievementHelper.isIncrementalAchievement(condition);
|
||||
|
||||
// Parse achievements
|
||||
for (var data : triggerList) {
|
||||
// Get achievement
|
||||
var achievement = this.getAchievement(data);
|
||||
|
||||
// Update achievement
|
||||
boolean changed = achievement.trigger(isTotal, progress, param1, param2);
|
||||
|
||||
// Sync with client if achievement was changed
|
||||
if (changed) {
|
||||
this.syncAchievement(achievement);
|
||||
}
|
||||
}
|
||||
|
||||
// Update total achievements
|
||||
if (this.hasCompleted) {
|
||||
this.hasCompleted = false;
|
||||
this.getPlayer().trigger(AchievementCondition.AchievementTotal, this.getCompletedAchievementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void triggerOne(int id, int progress, int param1, int param2) {
|
||||
// Get achievement data
|
||||
var data = GameData.getAchievementDataTable().get(id);
|
||||
if (data == null) return;
|
||||
|
||||
// Get achievement
|
||||
var achievement = this.getAchievement(data);
|
||||
|
||||
// Check what type of achievement condition this is
|
||||
boolean isTotal = AchievementHelper.isIncrementalAchievement(data.getCompleteCond());
|
||||
|
||||
// Update achievement
|
||||
boolean changed = achievement.trigger(isTotal, progress, param1, param2);
|
||||
|
||||
// Sync with client if achievement was changed
|
||||
if (changed) {
|
||||
this.syncAchievement(achievement);
|
||||
}
|
||||
|
||||
// Update total achievements
|
||||
if (this.hasCompleted) {
|
||||
this.hasCompleted = false;
|
||||
this.getPlayer().trigger(AchievementCondition.AchievementTotal, this.getCompletedAchievementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this achievement on the player client
|
||||
*/
|
||||
private void syncAchievement(GameAchievement achievement) {
|
||||
if (!getPlayer().hasSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set save flag
|
||||
this.queueSave = true;
|
||||
|
||||
// Check if achievement was completed
|
||||
if (achievement.isComplete()) {
|
||||
this.hasCompleted = true;
|
||||
}
|
||||
|
||||
// Send update to player
|
||||
getPlayer().addNextPackage(
|
||||
NetMsgId.achievement_change_notify,
|
||||
achievement.toProto()
|
||||
);
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo recvRewards(RepeatedInt ids) {
|
||||
// Sanity check
|
||||
if (ids.length() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Init variables
|
||||
var rewards = new ItemParamMap();
|
||||
|
||||
// Claim achievements
|
||||
for (int id : ids) {
|
||||
// Get achievement
|
||||
var achievement = this.getAchievements().get(id);
|
||||
if (achievement == null) continue;
|
||||
|
||||
// Check if we can claim this achievement
|
||||
if (achievement.isClaimable()) {
|
||||
// Claim
|
||||
achievement.setClaimed(true);
|
||||
|
||||
// Add rewards
|
||||
rewards.add(achievement.getData().getTid1(), achievement.getData().getQty1());
|
||||
|
||||
// Save
|
||||
this.queueSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
return this.getPlayer().getInventory().addItems(rewards);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public synchronized Achievements toProto() {
|
||||
var proto = Achievements.newInstance();
|
||||
|
||||
for (var achievement : this.getAchievements().values()) {
|
||||
proto.addList(achievement.toProto());
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
this.queueSave = false;
|
||||
}
|
||||
}
|
||||
122
src/main/java/emu/nebula/game/achievement/GameAchievement.java
Normal file
122
src/main/java/emu/nebula/game/achievement/GameAchievement.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package emu.nebula.game.achievement;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.AchievementDef;
|
||||
import emu.nebula.proto.Public.Achievement;
|
||||
import emu.nebula.proto.Public.QuestProgress;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class GameAchievement {
|
||||
private int id;
|
||||
private int curProgress;
|
||||
private long completed;
|
||||
private boolean claimed;
|
||||
|
||||
private transient AchievementDef data;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public GameAchievement() {
|
||||
|
||||
}
|
||||
|
||||
public GameAchievement(AchievementDef data) {
|
||||
this.id = data.getId();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public AchievementDef getData() {
|
||||
if (this.data == null) {
|
||||
this.data = GameData.getAchievementDataTable().get(this.getId());
|
||||
}
|
||||
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public int getMaxProgress() {
|
||||
return this.getData().getAimNumShow();
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return this.completed > 0;
|
||||
}
|
||||
|
||||
public boolean isClaimable() {
|
||||
return !this.isClaimed() && this.isComplete();
|
||||
}
|
||||
|
||||
public void setClaimed(boolean claimed) {
|
||||
this.claimed = claimed;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
if (this.isClaimed()) {
|
||||
return 2;
|
||||
} else if (this.isComplete()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if achievement was updated
|
||||
*/
|
||||
public boolean trigger(boolean isTotal, int progress, int param1, int param2) {
|
||||
// Sanity check
|
||||
if (this.isComplete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check param conditions
|
||||
var data = this.getData();
|
||||
if (data == null) return false;
|
||||
|
||||
if (data.hasParam1(param1) && data.getParam1() != param1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.hasParam2(param2) && data.getParam2() != param2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set previous progress
|
||||
int prevProgress = this.curProgress;
|
||||
|
||||
// Update progress
|
||||
if (isTotal) {
|
||||
this.curProgress += progress;
|
||||
} else {
|
||||
this.curProgress = progress;
|
||||
}
|
||||
|
||||
// Check if completed
|
||||
if (this.getCurProgress() >= this.getMaxProgress()) {
|
||||
this.curProgress = this.getMaxProgress();
|
||||
this.completed = Nebula.getCurrentTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if progress was changed
|
||||
return prevProgress != this.curProgress;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Achievement toProto() {
|
||||
var progress = QuestProgress.newInstance()
|
||||
.setCur(this.getCurProgress())
|
||||
.setMax(this.getMaxProgress());
|
||||
|
||||
var proto = Achievement.newInstance()
|
||||
.setId(this.getId())
|
||||
.setCompleted(this.getCompleted())
|
||||
.setStatus(this.getStatus())
|
||||
.addProgress(progress);
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
132
src/main/java/emu/nebula/game/activity/ActivityManager.java
Normal file
132
src/main/java/emu/nebula/game/activity/ActivityManager.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package emu.nebula.game.activity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.activity.type.*;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "activity", useDiscriminator = false)
|
||||
public class ActivityManager extends PlayerManager implements GameDatabaseObject {
|
||||
@Id
|
||||
private int uid;
|
||||
|
||||
// Achievement data
|
||||
private Map<Integer, GameActivity> activities;
|
||||
|
||||
@Setter
|
||||
private transient boolean queueSave;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public ActivityManager() {
|
||||
|
||||
}
|
||||
|
||||
public ActivityManager(Player player) {
|
||||
super(player);
|
||||
this.uid = player.getUid();
|
||||
this.activities = new HashMap<>();
|
||||
}
|
||||
|
||||
public <T extends GameActivity> T getActivity(Class<T> activityClass, int id) {
|
||||
// Get activity first
|
||||
var activity = this.getActivities().get(id);
|
||||
if (activity == null) return null;
|
||||
|
||||
// Check activity type
|
||||
if (activityClass.isInstance(activity)) {
|
||||
return activityClass.cast(activity);
|
||||
}
|
||||
|
||||
// Failure
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This needs to be called after being loaded from the database
|
||||
*/
|
||||
public synchronized void init() {
|
||||
// Check if any activities
|
||||
var it = this.getActivities().entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
var entry = it.next();
|
||||
var activity = entry.getValue();
|
||||
|
||||
// Validate the activity to make sure it exists
|
||||
var data = GameData.getActivityDataTable().get(activity.getId());
|
||||
if (data == null) {
|
||||
it.remove();
|
||||
this.queueSave = true;
|
||||
}
|
||||
|
||||
// Set data
|
||||
activity.setData(data);
|
||||
activity.setManager(this);
|
||||
}
|
||||
|
||||
// Load activities
|
||||
for (var id : Nebula.getGameContext().getActivityModule().getActivities()) {
|
||||
// Check if we already have this activity
|
||||
if (this.getActivities().containsKey(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create activity
|
||||
var activity = this.createActivity(id);
|
||||
|
||||
if (activity == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add activity
|
||||
this.getActivities().put(id, activity);
|
||||
|
||||
// Set save flag
|
||||
this.queueSave = true;
|
||||
}
|
||||
|
||||
// Save if any activities were changed
|
||||
if (this.queueSave) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
private GameActivity createActivity(int id) {
|
||||
// Get activity data first
|
||||
var data = GameData.getActivityDataTable().get(id);
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO improve activity creation
|
||||
GameActivity activity = switch (data.getType()) {
|
||||
case TowerDefense -> new TowerDefenseActivity(this, data);
|
||||
case Trial -> new TrialActivity(this, data);
|
||||
case Levels -> new LevelsActivity(this, data);
|
||||
case Task -> new TaskActivity(this, data);
|
||||
case Shop -> new ShopActivity(this, data);
|
||||
default -> null;
|
||||
};
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
this.queueSave = false;
|
||||
}
|
||||
}
|
||||
37
src/main/java/emu/nebula/game/activity/ActivityModule.java
Normal file
37
src/main/java/emu/nebula/game/activity/ActivityModule.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package emu.nebula.game.activity;
|
||||
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.GameContextModule;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class ActivityModule extends GameContextModule {
|
||||
private IntList activities;
|
||||
|
||||
public ActivityModule(GameContext context) {
|
||||
super(context);
|
||||
this.activities = new IntArrayList();
|
||||
|
||||
// Hardcode these activities for now
|
||||
// TODO make an activity json file to read activity ids from
|
||||
|
||||
// Trial activities
|
||||
this.activities.add(700104);
|
||||
this.activities.add(700107);
|
||||
this.activities.add(700108);
|
||||
|
||||
// Tower defense activity
|
||||
this.activities.add(102001);
|
||||
|
||||
//
|
||||
this.activities.add(1010201);
|
||||
this.activities.add(1010203);
|
||||
this.activities.add(1010204);
|
||||
|
||||
//this.activities.add(101002);
|
||||
//this.activities.add(101003);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package emu.nebula.game.activity;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import emu.nebula.data.resources.ActivityTaskDef;
|
||||
import emu.nebula.proto.Public.Quest;
|
||||
import emu.nebula.proto.Public.QuestProgress;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class ActivityTaskQuest {
|
||||
private int id;
|
||||
private int cond;
|
||||
|
||||
private int curProgress;
|
||||
private int maxProgress;
|
||||
|
||||
@Setter
|
||||
private boolean claimed;
|
||||
|
||||
@Deprecated
|
||||
public ActivityTaskQuest() {
|
||||
|
||||
}
|
||||
|
||||
public ActivityTaskQuest(ActivityTaskDef data) {
|
||||
this.id = data.getId();
|
||||
this.cond = data.getCompleteCond();
|
||||
this.maxProgress = data.getAimNumShow();
|
||||
}
|
||||
|
||||
public void resetProgress() {
|
||||
this.curProgress = 0;
|
||||
this.claimed = false;
|
||||
}
|
||||
|
||||
public boolean isComplete() {
|
||||
return this.curProgress >= this.maxProgress;
|
||||
}
|
||||
|
||||
private int getStatus() {
|
||||
if (this.isClaimed()) {
|
||||
return 2;
|
||||
} else if (this.isComplete()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean trigger(int condition, int progress, int param1, int param2) {
|
||||
// Sanity check
|
||||
if (this.isComplete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if not the correct condition
|
||||
if (this.cond != condition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check quest param TODO
|
||||
|
||||
// Get new progress
|
||||
int newProgress = Math.min(this.curProgress + progress, this.maxProgress);
|
||||
|
||||
// Set
|
||||
if (this.curProgress != newProgress) {
|
||||
this.curProgress = newProgress;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Quest toProto() {
|
||||
var progress = QuestProgress.newInstance()
|
||||
.setCur(this.getCurProgress())
|
||||
.setMax(this.getMaxProgress());
|
||||
|
||||
var proto = Quest.newInstance()
|
||||
.setId(this.getId())
|
||||
.setStatus(this.getStatus())
|
||||
.addProgress(progress);
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
40
src/main/java/emu/nebula/game/activity/ActivityType.java
Normal file
40
src/main/java/emu/nebula/game/activity/ActivityType.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package emu.nebula.game.activity;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ActivityType {
|
||||
None (0),
|
||||
PeriodicQuest (1),
|
||||
LoginReward (2),
|
||||
Mining (3),
|
||||
Cookie (4),
|
||||
TowerDefense (5),
|
||||
Trial (6),
|
||||
JointDrill (7),
|
||||
CG (8),
|
||||
Levels (9),
|
||||
Avg (10),
|
||||
Task (11),
|
||||
Shop (12),
|
||||
Advertise (13);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<ActivityType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (ActivityType type : ActivityType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private ActivityType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ActivityType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
65
src/main/java/emu/nebula/game/activity/GameActivity.java
Normal file
65
src/main/java/emu/nebula/game/activity/GameActivity.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package emu.nebula.game.activity;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import emu.nebula.proto.Public.Activity;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = true)
|
||||
public abstract class GameActivity {
|
||||
private int id;
|
||||
|
||||
@Setter private transient ActivityManager manager;
|
||||
@Setter private transient ActivityDef data;
|
||||
|
||||
@Deprecated // Morhpia only
|
||||
public GameActivity() {
|
||||
|
||||
}
|
||||
|
||||
public GameActivity(ActivityManager manager, ActivityDef data) {
|
||||
this.id = data.getId();
|
||||
this.manager = manager;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.getManager().getPlayer();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
Nebula.getGameDatabase().update(
|
||||
this.getManager(),
|
||||
this.getManager().getPlayerUid(),
|
||||
"activities." + this.getId(),
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public Activity toProto() {
|
||||
var proto = Activity.newInstance()
|
||||
.setId(this.getId())
|
||||
.setStartTime(1)
|
||||
.setEndTime(Integer.MAX_VALUE);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public ActivityMsg toMsgProto() {
|
||||
var proto = ActivityMsg.newInstance()
|
||||
.setId(this.getId());
|
||||
|
||||
this.encodeActivityMsg(proto);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public abstract void encodeActivityMsg(ActivityMsg msg);
|
||||
}
|
||||
209
src/main/java/emu/nebula/game/activity/type/LevelsActivity.java
Normal file
209
src/main/java/emu/nebula/game/activity/type/LevelsActivity.java
Normal file
@@ -0,0 +1,209 @@
|
||||
package emu.nebula.game.activity.type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.data.resources.ActivityLevelsLevelDef;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.activity.GameActivity;
|
||||
import emu.nebula.game.instance.InstanceSettleData;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import emu.nebula.proto.Public.ActivityLevel;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
public class LevelsActivity extends GameActivity {
|
||||
private Map<Integer, ActivityLevelInfo> levels;
|
||||
|
||||
// Apply level data
|
||||
private transient ActivityLevelsLevelDef level;
|
||||
private transient long buildId;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public LevelsActivity() {
|
||||
|
||||
}
|
||||
|
||||
public LevelsActivity(ActivityManager manager, ActivityDef data) {
|
||||
super(manager, data);
|
||||
this.levels = new HashMap<>();
|
||||
}
|
||||
|
||||
public boolean apply(int levelId, long buildId) {
|
||||
// Verify level
|
||||
var level = GameData.getActivityLevelsLevelDataTable().get(levelId);
|
||||
|
||||
if (level == null || level.getActivityId() != this.getId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify build
|
||||
var build = this.getPlayer().getStarTowerManager().getBuildById(buildId);
|
||||
|
||||
if (build == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set
|
||||
this.level = level;
|
||||
this.buildId = buildId;
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settle(int star) {
|
||||
// Check energy
|
||||
if (!this.getLevel().hasEnergy(this.getPlayer(), 1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate settle data
|
||||
var settleData = new InstanceSettleData();
|
||||
|
||||
settleData.setWin(star > 0);
|
||||
settleData.setFirst(settleData.isWin() && !this.getLevels().containsKey(this.getLevel().getId()));
|
||||
|
||||
// Init player change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Handle win
|
||||
if (settleData.isWin()) {
|
||||
// Calculate energy and exp
|
||||
settleData.setExp(this.getLevel().getEnergyConsume());
|
||||
getPlayer().consumeEnergy(settleData.getExp(), change);
|
||||
|
||||
// Calculate rewards
|
||||
settleData.generateRewards(this.getLevel());
|
||||
|
||||
// Add to inventory
|
||||
getPlayer().getInventory().addItem(GameConstants.EXP_ITEM_ID, settleData.getExp(), change);
|
||||
getPlayer().getInventory().addItems(settleData.getRewards(), change);
|
||||
getPlayer().getInventory().addItems(settleData.getFirstRewards(), change);
|
||||
|
||||
// Log
|
||||
var level = getLevels().computeIfAbsent(this.getLevel().getId(), i -> new ActivityLevelInfo());
|
||||
|
||||
level.setStar(star);
|
||||
level.setBuildId(this.getBuildId());
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Quest triggers
|
||||
this.getPlayer().trigger(QuestCondition.BattleTotal, 1);
|
||||
}
|
||||
|
||||
// Set extra data
|
||||
change.setExtraData(settleData);
|
||||
|
||||
// Success
|
||||
return change;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo sweep(ActivityLevelsLevelDef data, int count) {
|
||||
// Sanity check count
|
||||
if (count <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if we have 3 starred this instance
|
||||
var level = this.getLevels().get(data.getId());
|
||||
|
||||
if (level == null || level.getStar() != 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check energy cost
|
||||
int energyCost = data.getEnergyConsume() * count;
|
||||
|
||||
if (this.getPlayer().getEnergy() < energyCost) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Init variables
|
||||
var change = new PlayerChangeInfo();
|
||||
var list = new ArrayList<ItemParamMap>();
|
||||
|
||||
// Consume exp
|
||||
getPlayer().consumeEnergy(energyCost, change);
|
||||
getPlayer().getInventory().addItem(GameConstants.EXP_ITEM_ID, energyCost, change);
|
||||
|
||||
// Calculate total rewards
|
||||
var totalRewards = new ItemParamMap();
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Generate rewards for each settle count
|
||||
var rewards = data.getRewards().generate();
|
||||
|
||||
// Add to reward list
|
||||
list.add(rewards);
|
||||
|
||||
// Add to total rewards
|
||||
totalRewards.add(rewards);
|
||||
}
|
||||
|
||||
// Add total rewards to inventory
|
||||
getPlayer().getInventory().addItems(totalRewards, change);
|
||||
|
||||
// Set reward list in change info so we can serialize it in the response proto later
|
||||
change.setExtraData(list);
|
||||
|
||||
// Quest triggers
|
||||
this.getPlayer().trigger(QuestCondition.BattleTotal, count);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeActivityMsg(ActivityMsg msg) {
|
||||
var proto = msg.getMutableLevels();
|
||||
|
||||
for (var entry : this.getLevels().entrySet()) {
|
||||
int id = entry.getKey();
|
||||
var level = entry.getValue();
|
||||
|
||||
var info = level.toProto()
|
||||
.setId(id);
|
||||
|
||||
proto.addLevels(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public static class ActivityLevelInfo {
|
||||
private int star;
|
||||
private long buildId;
|
||||
|
||||
public ActivityLevelInfo() {
|
||||
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public ActivityLevel toProto() {
|
||||
var proto = ActivityLevel.newInstance()
|
||||
.setStar(this.getStar())
|
||||
.setBuildId(this.getBuildId());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
}
|
||||
152
src/main/java/emu/nebula/game/activity/type/ShopActivity.java
Normal file
152
src/main/java/emu/nebula/game/activity/type/ShopActivity.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package emu.nebula.game.activity.type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.data.resources.ActivityShopControlDef;
|
||||
import emu.nebula.data.resources.ActivityShopDef;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.activity.GameActivity;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import emu.nebula.proto.Public.BoughtGoods;
|
||||
import emu.nebula.proto.Public.ResidentShop;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
public class ShopActivity extends GameActivity {
|
||||
private Map<Integer, ActivityShopInfo> shops;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public ShopActivity() {
|
||||
|
||||
}
|
||||
|
||||
public ShopActivity(ActivityManager manager, ActivityDef data) {
|
||||
super(manager, data);
|
||||
this.shops = new HashMap<>();
|
||||
|
||||
// Load shops
|
||||
var control = GameData.getActivityShopControlDataTable().get(data.getId());
|
||||
if (control != null) {
|
||||
this.initShops(control);
|
||||
}
|
||||
}
|
||||
|
||||
private void initShops(ActivityShopControlDef control) {
|
||||
for (int id : control.getShopIds()) {
|
||||
var data = GameData.getActivityShopDataTable().get(id);
|
||||
if (data == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create resident shop
|
||||
var shop = new ActivityShopInfo(data);
|
||||
|
||||
// Add
|
||||
this.getShops().put(data.getId(), shop);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerChangeInfo buy(int shopId, int goodsId, int count) {
|
||||
// Get shop
|
||||
var shop = this.getShops().get(shopId);
|
||||
if (shop == null) return null;
|
||||
|
||||
// Get shop data
|
||||
var data = GameData.getActivityShopDataTable().get(shopId);
|
||||
if (data == null) return null;
|
||||
|
||||
// Get goods
|
||||
var goods = data.getGoods().get(goodsId);
|
||||
if (goods == null) return null;
|
||||
|
||||
// Check limit
|
||||
if (goods.getMaximumLimit() > 0) {
|
||||
var limit = goods.getMaximumLimit() - shop.getBoughtCount(goodsId);
|
||||
|
||||
if (count > limit) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Purchase
|
||||
var change = getPlayer().getInventory().buyItem(data.getCurrencyItemId(), goods.getPrice(), goods.getItems(), count);
|
||||
|
||||
// Purchase failed
|
||||
if (change == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set log
|
||||
shop.getBoughtGoods().add(goodsId, count);
|
||||
|
||||
// Save
|
||||
this.save();
|
||||
|
||||
// Set extra data
|
||||
change.setExtraData(shop);
|
||||
|
||||
// Success
|
||||
return change;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeActivityMsg(ActivityMsg msg) {
|
||||
var proto = msg.getMutableShop();
|
||||
|
||||
for (var entry : this.getShops().entrySet()) {
|
||||
var id = entry.getKey();
|
||||
var shop = entry.getValue();
|
||||
|
||||
var info = shop.toProto()
|
||||
.setId(id);
|
||||
|
||||
proto.addShops(info);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public static class ActivityShopInfo {
|
||||
private ItemParamMap boughtGoods;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public ActivityShopInfo() {
|
||||
|
||||
}
|
||||
|
||||
public ActivityShopInfo(ActivityShopDef data) {
|
||||
this.boughtGoods = new ItemParamMap();
|
||||
}
|
||||
|
||||
public int getBoughtCount(int goodsId) {
|
||||
return this.getBoughtGoods().get(goodsId);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public ResidentShop toProto() {
|
||||
var proto = ResidentShop.newInstance();
|
||||
|
||||
for (var item : this.getBoughtGoods().int2IntEntrySet()) {
|
||||
var info = BoughtGoods.newInstance()
|
||||
.setId(item.getIntKey())
|
||||
.setNumber(item.getIntValue());
|
||||
|
||||
proto.addInfos(info);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package emu.nebula.game.activity.type;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.activity.ActivityTaskQuest;
|
||||
import emu.nebula.game.activity.GameActivity;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
public class TaskActivity extends GameActivity {
|
||||
private Map<Integer, ActivityTaskQuest> quests;
|
||||
private IntSet completedGroups;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public TaskActivity() {
|
||||
|
||||
}
|
||||
|
||||
public TaskActivity(ActivityManager manager, ActivityDef data) {
|
||||
super(manager, data);
|
||||
this.quests = new HashMap<>();
|
||||
this.completedGroups = new IntOpenHashSet();
|
||||
|
||||
var groupSet = new IntOpenHashSet();
|
||||
for (var group : GameData.getActivityTaskGroupDataTable()) {
|
||||
if (group.getActivityId() != this.getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
groupSet.add(group.getId());
|
||||
}
|
||||
|
||||
for (var task : GameData.getActivityTaskDataTable()) {
|
||||
if (!groupSet.contains(task.getActivityTaskGroupId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var quest = new ActivityTaskQuest(task);
|
||||
|
||||
this.getQuests().put(quest.getId(), quest);
|
||||
}
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeActivityMsg(ActivityMsg msg) {
|
||||
var proto = msg.getMutableTask();
|
||||
|
||||
for (int id : this.getCompletedGroups()) {
|
||||
proto.addGroupIds(id);
|
||||
}
|
||||
|
||||
var tasks = proto.getMutableActivityTasks();
|
||||
|
||||
for (var quest : this.getQuests().values()) {
|
||||
tasks.addList(quest.toProto());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package emu.nebula.game.activity.type;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.activity.GameActivity;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import emu.nebula.proto.Public.ActivityQuest;
|
||||
import emu.nebula.proto.Public.ActivityTowerDefenseLevel;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
public class TowerDefenseActivity extends GameActivity {
|
||||
private Int2IntMap completedStages;
|
||||
private Int2IntMap completedQuests;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public TowerDefenseActivity() {
|
||||
|
||||
}
|
||||
|
||||
public TowerDefenseActivity(ActivityManager manager, ActivityDef data) {
|
||||
super(manager, data);
|
||||
this.completedStages = new Int2IntOpenHashMap();
|
||||
this.completedQuests = new Int2IntOpenHashMap();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo claimReward(int level) {
|
||||
// Get rewards
|
||||
var rewards = GameData.getTowerDefenseLevelDataTable().get(level).getRewards();
|
||||
|
||||
// Add rewards
|
||||
return getPlayer().getInventory().addItems(rewards);
|
||||
}
|
||||
|
||||
// public PlayerChangeInfo claimReward(int groupId) {
|
||||
// // Create change info
|
||||
// var change = new PlayerChangeInfo();
|
||||
|
||||
// // Make sure we haven't completed this group yet
|
||||
// if (this.getCompleted().contains(groupId)) {
|
||||
// return change;
|
||||
// }
|
||||
|
||||
// // Get trial control
|
||||
// var control = GameData.getTrialControlDataTable().get(this.getId());
|
||||
// if (control == null) return change;
|
||||
|
||||
// // Get group
|
||||
// var group = GameData.getTrialGroupDataTable().get(groupId);
|
||||
// if (group == null) return change;
|
||||
|
||||
// // Set as completed
|
||||
// this.getCompleted().add(groupId);
|
||||
|
||||
// // Save to database
|
||||
// this.save();
|
||||
|
||||
// // Add rewards
|
||||
// return getPlayer().getInventory().addItems(group.getRewards(), change);
|
||||
// }
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeActivityMsg(ActivityMsg msg) {
|
||||
var proto = msg.getMutableTowerDefense();
|
||||
|
||||
// Add completed stages
|
||||
for (int id : this.completedStages.keySet()) {
|
||||
// Create proto
|
||||
var level = ActivityTowerDefenseLevel.newInstance();
|
||||
|
||||
// Set proto params
|
||||
level.setId(id);
|
||||
level.setStar(this.completedStages.get(id));
|
||||
|
||||
// Add to final msg proto
|
||||
proto.addLevels(level);
|
||||
}
|
||||
|
||||
// Add completed quests
|
||||
for (int id : this.completedStages.keySet()) {
|
||||
// Create proto
|
||||
var quest = ActivityQuest.newInstance();
|
||||
|
||||
// Set proto params
|
||||
quest.setActivityId(this.getId());
|
||||
quest.setId(id);
|
||||
quest.setStatus(2); // TODO: properly handle event quests
|
||||
|
||||
// Add to final msg proto
|
||||
proto.addQuests(quest);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package emu.nebula.game.activity.type;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ActivityDef;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.activity.GameActivity;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.ActivityDetail.ActivityMsg;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity
|
||||
public class TrialActivity extends GameActivity {
|
||||
private IntList completed;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public TrialActivity() {
|
||||
|
||||
}
|
||||
|
||||
public TrialActivity(ActivityManager manager, ActivityDef data) {
|
||||
super(manager, data);
|
||||
this.completed = new IntArrayList();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo claimReward(int groupId) {
|
||||
// Create change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Make sure we haven't completed this group yet
|
||||
if (this.getCompleted().contains(groupId)) {
|
||||
return change;
|
||||
}
|
||||
|
||||
// Get trial control
|
||||
var control = GameData.getTrialControlDataTable().get(this.getId());
|
||||
if (control == null) return change;
|
||||
|
||||
// Get group
|
||||
var group = GameData.getTrialGroupDataTable().get(groupId);
|
||||
if (group == null) return change;
|
||||
|
||||
// Set as completed
|
||||
this.getCompleted().add(groupId);
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Add rewards
|
||||
return getPlayer().getInventory().addItems(group.getRewards(), change);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeActivityMsg(ActivityMsg msg) {
|
||||
var proto = msg.getMutableTrial();
|
||||
|
||||
for (int id : this.getCompleted()) {
|
||||
proto.addCompletedGroupIds(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,11 +8,12 @@ import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import lombok.Getter;
|
||||
import us.hebi.quickbuf.RepeatedInt;
|
||||
|
||||
@@ -84,7 +85,7 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
|
||||
this.getAgents().put(agent.getId(), agent);
|
||||
|
||||
// Quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.AgentApplyTotal, 1);
|
||||
this.getPlayer().trigger(QuestCondition.AgentApplyTotal, 1);
|
||||
|
||||
// Success
|
||||
return agent;
|
||||
@@ -182,8 +183,9 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.AgentFinishTotal, list.size());
|
||||
// Quest + Achievements
|
||||
getPlayer().trigger(QuestCondition.AgentFinishTotal, list.size());
|
||||
getPlayer().trigger(AchievementCondition.AgentWithSpecificFinishTotal, list.size());
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
|
||||
63
src/main/java/emu/nebula/game/ban/BanInfo.java
Normal file
63
src/main/java/emu/nebula/game/ban/BanInfo.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package emu.nebula.game.ban;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.PlayerErrorCode;
|
||||
import emu.nebula.proto.Public.Error;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "bans", useDiscriminator = false)
|
||||
public class BanInfo implements GameDatabaseObject {
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
private int playerUid;
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
private String reason;
|
||||
private String bannedBy;
|
||||
private String ipAddress;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BanInfo() {
|
||||
}
|
||||
|
||||
public BanInfo(int playerUid, long endTime, String reason, String bannedBy, String ipAddress) {
|
||||
this.playerUid = playerUid;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.endTime = endTime;
|
||||
this.reason = reason;
|
||||
this.bannedBy = bannedBy;
|
||||
this.ipAddress = ipAddress;
|
||||
// Generate ID based on either player UID or IP address
|
||||
this.id = (ipAddress != null && !ipAddress.isEmpty() && playerUid == 0) ? "ip_" + ipAddress :
|
||||
"player_" + playerUid;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return endTime != 0 && endTime < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getExpirationDateString() {
|
||||
if (endTime == 0) {
|
||||
return "Never";
|
||||
}
|
||||
return Utils.formatTimestamp(this.endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
GameDatabaseObject.super.save();
|
||||
}
|
||||
|
||||
public Error toProto() {
|
||||
return Error.newInstance()
|
||||
.setCode(PlayerErrorCode.ErrBan.getValue())
|
||||
.addArguments(
|
||||
getExpirationDateString() + "\n" +
|
||||
(this.reason != null ? "\n (" + this.reason + ")" : "\n" + this.id));
|
||||
}
|
||||
}
|
||||
242
src/main/java/emu/nebula/game/ban/BanModule.java
Normal file
242
src/main/java/emu/nebula/game/ban/BanModule.java
Normal file
@@ -0,0 +1,242 @@
|
||||
package emu.nebula.game.ban;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.GameContextModule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BanModule extends GameContextModule {
|
||||
private final Map<String, BanInfo> cachedIpBans = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, BanInfo> cachedPlayerBans = new ConcurrentHashMap<>();
|
||||
|
||||
public BanModule(GameContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ban a player by UID
|
||||
* @param uid Player UID
|
||||
* @param endTime Ban expiration time (0 = permanent)
|
||||
* @param isBanIp Ban Ip
|
||||
* @param reason Ban reason
|
||||
* @param bannedBy Who banned the player
|
||||
*/
|
||||
public void banPlayer(int uid, long endTime, String reason, boolean isBanIp, String bannedBy) {
|
||||
Player player = getGameContext().getPlayerModule().getPlayer(uid);
|
||||
|
||||
// It is only used as a supplement to find the banned IP when unblocking a player
|
||||
String playerBindIp = null;
|
||||
|
||||
if (isBanIp) {
|
||||
if (player != null && player.getSession() != null) {
|
||||
String ipAddress = player.getSession().getIpAddress();
|
||||
if (ipAddress != null && !ipAddress.isEmpty()) {
|
||||
playerBindIp = ipAddress;
|
||||
banIp(ipAddress, endTime, reason, bannedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BanInfo banInfo = new BanInfo(uid, endTime, reason, bannedBy, playerBindIp);
|
||||
cachedPlayerBans.put(uid, banInfo);
|
||||
banInfo.save();
|
||||
|
||||
// Kick player
|
||||
if (player != null && player.isLoaded()) {
|
||||
player.setSession(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ban an IP address
|
||||
* <p>
|
||||
* Please be cautious about disabling IPs
|
||||
* in some regions where IPs are scarce, many people may share a public IP
|
||||
* and restarting the optical cat device will reassign a new IP
|
||||
*
|
||||
* @param ipAddress IP address to ban
|
||||
* @param endTime Ban expiration time (0 = permanent)
|
||||
* @param reason Ban reason
|
||||
* @param bannedBy Who banned the IP
|
||||
*/
|
||||
public void banIp(String ipAddress, long endTime, String reason, String bannedBy) {
|
||||
BanInfo banInfo = new BanInfo(0, endTime, reason, bannedBy, ipAddress);
|
||||
cachedIpBans.put(ipAddress, banInfo);
|
||||
banInfo.save();
|
||||
|
||||
List<Player> playerList = Nebula.getGameContext().getPlayerModule().getCachedPlayers()
|
||||
.values().stream().toList();
|
||||
|
||||
String playerIpAddress;
|
||||
for (Player player : playerList) {
|
||||
playerIpAddress = player.getSession().getIpAddress();
|
||||
if (playerIpAddress == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kick player
|
||||
if (playerIpAddress.equals(ipAddress)) {
|
||||
player.setSession(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unban a player
|
||||
* @param uid Player UID
|
||||
*/
|
||||
public void unbanPlayer(int uid) {
|
||||
BanInfo banInfo = cachedPlayerBans.remove(uid);
|
||||
if (banInfo == null) {
|
||||
banInfo = getPlayerBanInfo(uid);
|
||||
}
|
||||
|
||||
if (banInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteBan(banInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unban an IP address
|
||||
* @param ipAddress IP address to unban
|
||||
*/
|
||||
public void unbanIp(String ipAddress) {
|
||||
BanInfo banInfo = cachedIpBans.remove(ipAddress);
|
||||
if (banInfo == null) {
|
||||
banInfo = getIpBanInfo(ipAddress);
|
||||
}
|
||||
|
||||
if (banInfo == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteBan(banInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a player is banned
|
||||
* @param uid Player UID
|
||||
* @return True if banned, false otherwise
|
||||
*/
|
||||
public boolean isPlayerBanned(int uid) {
|
||||
BanInfo banInfo = cachedPlayerBans.get(uid);
|
||||
if (banInfo == null) {
|
||||
banInfo = loadPlayerBanFromDatabase(uid);
|
||||
if (banInfo == null) {
|
||||
return false;
|
||||
}
|
||||
cachedPlayerBans.put(uid, banInfo);
|
||||
}
|
||||
|
||||
// Check if ban has expired
|
||||
if (banInfo.isExpired()) {
|
||||
unbanPlayer(uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP address is banned
|
||||
* @param ipAddress IP address
|
||||
* @return True if banned, false otherwise
|
||||
*/
|
||||
public boolean isIpBanned(String ipAddress) {
|
||||
BanInfo banInfo = cachedIpBans.get(ipAddress);
|
||||
if (banInfo == null) {
|
||||
banInfo = loadIpBanFromDatabase(ipAddress);
|
||||
if (banInfo == null) {
|
||||
return false;
|
||||
}
|
||||
cachedIpBans.put(ipAddress, banInfo);
|
||||
}
|
||||
|
||||
// Check if ban has expired
|
||||
if (banInfo.isExpired()) {
|
||||
unbanIp(ipAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ban info for a player
|
||||
* @param uid Player UID
|
||||
* @return BanInfo or null if not banned
|
||||
*/
|
||||
public BanInfo getPlayerBanInfo(int uid) {
|
||||
BanInfo banInfo = cachedPlayerBans.get(uid);
|
||||
if (banInfo == null) {
|
||||
banInfo = loadPlayerBanFromDatabase(uid);
|
||||
if (banInfo != null) {
|
||||
cachedPlayerBans.put(uid, banInfo);
|
||||
}
|
||||
}
|
||||
return banInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ban info for an IP
|
||||
* @param ipAddress IP address
|
||||
* @return BanInfo or null if not banned
|
||||
*/
|
||||
public BanInfo getIpBanInfo(String ipAddress) {
|
||||
BanInfo banInfo = cachedIpBans.get(ipAddress);
|
||||
if (banInfo == null) {
|
||||
banInfo = loadIpBanFromDatabase(ipAddress);
|
||||
if (banInfo != null) {
|
||||
cachedIpBans.put(ipAddress, banInfo);
|
||||
}
|
||||
}
|
||||
return banInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete ban from database
|
||||
*/
|
||||
private void deleteBan(BanInfo banInfo) {
|
||||
try {
|
||||
Nebula.getGameDatabase().delete(banInfo);
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Failed to delete ban info from database", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load player ban from database
|
||||
*/
|
||||
private BanInfo loadPlayerBanFromDatabase(int uid) {
|
||||
if (uid == 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return Nebula.getGameDatabase().getObjectByField(BanInfo.class, "playerUid", uid);
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Failed to load player ban from database", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load IP ban from database
|
||||
*/
|
||||
private BanInfo loadIpBanFromDatabase(String ipAddress) {
|
||||
if (ipAddress == null || ipAddress.isEmpty())
|
||||
return null;
|
||||
|
||||
try {
|
||||
return Nebula.getGameDatabase().getObjectByField(BanInfo.class, "ipAddress", ipAddress);
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Failed to load IP ban from database", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.quest.GameQuest;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestType;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.BattlePassInfoOuterClass.BattlePassInfo;
|
||||
@@ -31,7 +30,7 @@ public class BattlePass implements GameDatabaseObject {
|
||||
private int uid;
|
||||
private transient BattlePassManager manager;
|
||||
|
||||
private int battlePassId;
|
||||
private int battlePassId; // Season id
|
||||
private int mode;
|
||||
private int level;
|
||||
private int exp;
|
||||
@@ -72,6 +71,13 @@ public class BattlePass implements GameDatabaseObject {
|
||||
return manager.getPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode directly
|
||||
*/
|
||||
public synchronized void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public boolean isPremium() {
|
||||
return this.mode > 0;
|
||||
}
|
||||
@@ -80,6 +86,14 @@ public class BattlePass implements GameDatabaseObject {
|
||||
return GameData.getBattlePassRewardDataTable().get((this.getBattlePassId() << 16) + level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the level directly, use getMaxExp() instead if adding exp.
|
||||
*/
|
||||
public synchronized void setLevel(int level) {
|
||||
this.level = level;
|
||||
this.exp = 0;
|
||||
}
|
||||
|
||||
public int getMaxExp() {
|
||||
var data = GameData.getBattlePassLevelDataTable().get(this.getLevel() + 1);
|
||||
return data != null ? data.getExp() : 0;
|
||||
@@ -101,6 +115,32 @@ public class BattlePass implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any rewards or quests are claimable
|
||||
*/
|
||||
public synchronized boolean hasNew() {
|
||||
// Check if any quests are complete but unclaimed
|
||||
for (var quest : getQuests().values()) {
|
||||
if (quest.isComplete() && !quest.isClaimed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we have any pending rewards
|
||||
for (int i = 1; i <= this.getLevel(); i++) {
|
||||
if (!this.getBasicReward().isSet(i)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.isPremium() && !this.getPremiumReward().isSet(i)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No claimable things
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized void resetDailyQuests(boolean resetWeekly) {
|
||||
// Reset daily quests
|
||||
for (var data : GameData.getBattlePassQuestDataTable()) {
|
||||
@@ -119,14 +159,19 @@ public class BattlePass implements GameDatabaseObject {
|
||||
this.syncQuest(quest);
|
||||
}
|
||||
|
||||
// Reset weekly limit for exp
|
||||
if (resetWeekly) {
|
||||
this.expWeek = 0;
|
||||
}
|
||||
|
||||
// Persist to database
|
||||
this.save();
|
||||
}
|
||||
|
||||
public synchronized void trigger(QuestCondType condition, int progress, int param) {
|
||||
public synchronized void trigger(int condition, int progress, int param1, int param2) {
|
||||
for (var quest : getQuests().values()) {
|
||||
// Try to trigger quest
|
||||
boolean result = quest.trigger(condition, progress, param);
|
||||
boolean result = quest.trigger(condition, progress, param1, param2);
|
||||
|
||||
// Skip if quest progress wasn't changed
|
||||
if (!result) {
|
||||
|
||||
@@ -12,6 +12,10 @@ public class BattlePassManager extends PlayerManager {
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean hasNew() {
|
||||
return this.getBattlePass().hasNew();
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.util.Map;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.resources.ChatDef;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.Contacts;
|
||||
@@ -125,6 +126,9 @@ public class CharacterContact {
|
||||
);
|
||||
}
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getCharacter().getPlayer().trigger(AchievementCondition.ChatTotal, 1);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@ import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.CharacterDef;
|
||||
import emu.nebula.data.resources.DiscDef;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Public.HandbookInfo;
|
||||
import emu.nebula.util.Bitset;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@@ -22,6 +24,10 @@ public class CharacterStorage extends PlayerManager {
|
||||
private final Int2ObjectMap<GameCharacter> characters;
|
||||
private final Int2ObjectMap<GameDisc> discs;
|
||||
|
||||
// Flags
|
||||
@Setter private boolean hasAddedChar;
|
||||
@Setter private boolean hasAddedDisc;
|
||||
|
||||
@Setter private boolean updateCharHandbook;
|
||||
@Setter private boolean updateDiscHandbook;
|
||||
|
||||
@@ -52,26 +58,39 @@ public class CharacterStorage extends PlayerManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.addCharacter(GameData.getCharacterDataTable().get(charId));
|
||||
// Get data
|
||||
var data = GameData.getCharacterDataTable().get(charId);
|
||||
if (data == null) return null;
|
||||
|
||||
// Add character
|
||||
return this.addCharacter(data);
|
||||
}
|
||||
|
||||
private GameCharacter addCharacter(CharacterDef data) {
|
||||
public GameCharacter addCharacter(CharacterDef data) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
if (this.hasCharacter(data.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prevent players from getting unavliable characters
|
||||
if (!data.isAvailable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create character
|
||||
var character = new GameCharacter(this.getPlayer(), data);
|
||||
|
||||
// Save to database
|
||||
character.save();
|
||||
|
||||
// Set flag for player to update character skins in their handbook
|
||||
this.setUpdateCharHandbook(true);
|
||||
|
||||
// Add to characters
|
||||
this.characters.put(character.getCharId(), character);
|
||||
|
||||
// Set flags for player to update character skins in their handbook
|
||||
this.setUpdateCharHandbook(true);
|
||||
this.setHasAddedChar(true);
|
||||
|
||||
// Return character
|
||||
return character;
|
||||
}
|
||||
|
||||
@@ -79,18 +98,6 @@ public class CharacterStorage extends PlayerManager {
|
||||
return this.getCharacters().values();
|
||||
}
|
||||
|
||||
public int getNewPhoneMessageCount() {
|
||||
int count = 0;
|
||||
|
||||
for (var character : this.getCharacterCollection()) {
|
||||
if (character.getContact().hasNew()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public HandbookInfo getCharacterHandbook() {
|
||||
var bitset = new Bitset();
|
||||
|
||||
@@ -130,26 +137,39 @@ public class CharacterStorage extends PlayerManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.addDisc(GameData.getDiscDataTable().get(discId));
|
||||
// Get data
|
||||
var data = GameData.getDiscDataTable().get(discId);
|
||||
if (data == null) return null;
|
||||
|
||||
// Add disc
|
||||
return this.addDisc(data);
|
||||
}
|
||||
|
||||
private GameDisc addDisc(DiscDef data) {
|
||||
// Sanity check to make sure we dont have this character already
|
||||
public GameDisc addDisc(DiscDef data) {
|
||||
// Sanity check to make sure we dont have this disc already
|
||||
if (this.hasDisc(data.getId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prevent players from getting unavliable discs
|
||||
if (!data.isAvailable()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create disc
|
||||
var disc = new GameDisc(this.getPlayer(), data);
|
||||
|
||||
// Save to database
|
||||
disc.save();
|
||||
|
||||
// Set flag for player to update discs in their handbook
|
||||
this.setUpdateDiscHandbook(true);
|
||||
|
||||
// Add to discs
|
||||
this.discs.put(disc.getDiscId(), disc);
|
||||
|
||||
// Set flags for player to update discs in their handbook
|
||||
this.setUpdateDiscHandbook(true);
|
||||
this.setHasAddedDisc(true);
|
||||
|
||||
// Return disc
|
||||
return disc;
|
||||
}
|
||||
|
||||
@@ -215,6 +235,73 @@ public class CharacterStorage extends PlayerManager {
|
||||
return change.setExtraData(modifiedDiscs);
|
||||
}
|
||||
|
||||
// Contacts
|
||||
|
||||
public int getNewPhoneMessageCount() {
|
||||
int count = 0;
|
||||
|
||||
for (var character : this.getCharacterCollection()) {
|
||||
if (character.getContact().hasNew()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Checks if we should add next packages for player
|
||||
*/
|
||||
public void checkPlayerState() {
|
||||
// Check if we need to trigger character achievements
|
||||
if (this.hasAddedChar) {
|
||||
this.hasAddedChar = false;
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.CharactersWithSpecificQuantityAndRarity,
|
||||
getCharacters().size()
|
||||
);
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.CharactersWithSpecificQuantityAndRarity,
|
||||
(int) getCharacters().values().stream().filter(GameCharacter::isMaster).count(),
|
||||
1,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we need to send handbook update to player
|
||||
if (this.updateCharHandbook) {
|
||||
this.updateCharHandbook = false;
|
||||
this.getPlayer().addNextPackage(
|
||||
NetMsgId.handbook_change_notify,
|
||||
this.getPlayer().getCharacters().getCharacterHandbook());
|
||||
}
|
||||
|
||||
// Check if we need to trigger disc achievements
|
||||
if (this.hasAddedChar) {
|
||||
this.hasAddedChar = false;
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.DiscAcquireSpecificQuantityAndRarity,
|
||||
this.getDiscs().size()
|
||||
);
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.DiscAcquireSpecificQuantityAndRarity,
|
||||
(int) getDiscs().values().stream().filter(GameDisc::isMaster).count(),
|
||||
1,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
// Check if we need to send handbook update to player
|
||||
if (this.updateDiscHandbook) {
|
||||
this.updateDiscHandbook = false;
|
||||
this.getPlayer().addNextPackage(
|
||||
NetMsgId.handbook_change_notify,
|
||||
this.getPlayer().getCharacters().getDiscHandbook());
|
||||
}
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
|
||||
@@ -7,10 +7,10 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public enum ElementType {
|
||||
INHERIT (0),
|
||||
WIND (1, 90020),
|
||||
AQUA (1, 90018),
|
||||
FIRE (2, 90019),
|
||||
EARTH (3, 90021),
|
||||
AQUA (4, 90018),
|
||||
WIND (4, 90020),
|
||||
LIGHT (5, 90022),
|
||||
DARK (6, 90023),
|
||||
NONE (7);
|
||||
|
||||
@@ -18,10 +18,11 @@ import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.CharacterDef;
|
||||
import emu.nebula.data.resources.TalentGroupDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Notify.Skin;
|
||||
import emu.nebula.proto.Notify.SkinChange;
|
||||
@@ -32,6 +33,7 @@ import emu.nebula.proto.Public.CharGemSlot;
|
||||
import emu.nebula.proto.Public.UI32;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerChar;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerCharGem;
|
||||
import emu.nebula.server.error.ServerException;
|
||||
import emu.nebula.util.Bitset;
|
||||
import emu.nebula.util.CustomIntArray;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
@@ -117,6 +119,10 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaster() {
|
||||
return this.getData().getGrade() == 1;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
@@ -186,7 +192,7 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
// Check if we leveled up
|
||||
if (this.level > oldLevel) {
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.CharacterUpTotal, this.level - oldLevel);
|
||||
this.getPlayer().trigger(QuestCondition.CharacterUpTotal, this.level - oldLevel);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
@@ -272,6 +278,9 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(AchievementCondition.CharacterAdvanceTotal, 1);
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
@@ -459,8 +468,8 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
// Add affinity exp
|
||||
this.addAffinityExp(exp);
|
||||
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.GiftGiveTotal, count);
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(QuestCondition.GiftGiveTotal, count);
|
||||
|
||||
// Remove items
|
||||
var change = this.getPlayer().getInventory().removeItems(items);
|
||||
@@ -652,16 +661,16 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized PlayerChangeInfo generateGem(int slotId) {
|
||||
public synchronized PlayerChangeInfo generateGem(int slotId) throws ServerException {
|
||||
// Get gem slot
|
||||
var slot = this.getGemSlot(slotId);
|
||||
if (slot == null) {
|
||||
return null;
|
||||
throw new ServerException(110105, "Emblem slot doesn't exist");
|
||||
}
|
||||
|
||||
// Skip if slot is full
|
||||
if (slot.isFull()) {
|
||||
return null;
|
||||
throw new ServerException(110105, "Emblem slots are full");
|
||||
}
|
||||
|
||||
// Get gem data
|
||||
@@ -670,12 +679,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
|
||||
// Check character level
|
||||
if (this.getLevel() < gemControl.getUnlockLevel()) {
|
||||
return null;
|
||||
throw new ServerException(110105, "Trekker needs to be at least level " + gemControl.getUnlockLevel());
|
||||
}
|
||||
|
||||
// Make sure the player has the materials to craft the emblem
|
||||
if (!getPlayer().getInventory().hasItem(gemData.getGenerateCostTid(), gemControl.getGeneratenCostQty())) {
|
||||
return null;
|
||||
throw new ServerException(119903);
|
||||
}
|
||||
|
||||
// Generate attributes and create gem
|
||||
@@ -699,10 +708,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public synchronized PlayerChangeInfo refreshGem(int slotId, int gemIndex, RepeatedInt lockedAttributes) {
|
||||
public synchronized PlayerChangeInfo refreshGem(int slotId, int gemIndex, RepeatedInt lockedAttributes) throws ServerException {
|
||||
// Get gem from slot
|
||||
var gem = this.getGemFromSlot(slotId, gemIndex);
|
||||
if (gem == null) return null;
|
||||
if (gem == null) {
|
||||
throw new ServerException(111609);
|
||||
}
|
||||
|
||||
// Get gem data
|
||||
var gemData = this.getData().getCharGemData(slotId);
|
||||
@@ -710,12 +721,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
|
||||
// Check character level
|
||||
if (this.getLevel() < gemControl.getUnlockLevel()) {
|
||||
return null;
|
||||
throw new ServerException(110105, "Trekker needs to be at least level " + gemControl.getUnlockLevel());
|
||||
}
|
||||
|
||||
// Get locked attributes
|
||||
if (lockedAttributes.length() > gemControl.getLockableNum()) {
|
||||
return null;
|
||||
throw new ServerException(110105, "You can only lock up to " + gemControl.getLockableNum() + " attributes");
|
||||
}
|
||||
|
||||
// Calculate the materials we need
|
||||
|
||||
@@ -11,10 +11,11 @@ import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.DiscDef;
|
||||
import emu.nebula.data.resources.SubNoteSkillPromoteGroupDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.proto.Public.Disc;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerDisc;
|
||||
|
||||
@@ -70,6 +71,10 @@ public class GameDisc implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaster() {
|
||||
return GameData.getItemDataTable().get(this.getDiscId()).getRarity() == 1;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
@@ -145,7 +150,7 @@ public class GameDisc implements GameDatabaseObject {
|
||||
// Check if we leveled up
|
||||
if (this.level > oldLevel) {
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.DiscStrengthenTotal, this.level - oldLevel);
|
||||
this.getPlayer().trigger(QuestCondition.DiscStrengthenTotal, this.level - oldLevel);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
@@ -215,6 +220,9 @@ public class GameDisc implements GameDatabaseObject {
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(AchievementCondition.DiscPromoteTotal, 1);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
@@ -237,9 +245,11 @@ public class GameDisc implements GameDatabaseObject {
|
||||
// Remove items
|
||||
var change = this.getPlayer().getInventory().removeItems(materials, null);
|
||||
|
||||
// Add star
|
||||
// Cache old star value
|
||||
int old = this.star;
|
||||
this.star = Math.max(this.star + count, 5);
|
||||
|
||||
// Add star
|
||||
this.star = Math.min(this.star + count, 5);
|
||||
|
||||
// Save to database if star count changed
|
||||
if (this.star != old) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -26,8 +26,8 @@ public class DatingManager extends PlayerManager {
|
||||
// Set landmark + character
|
||||
this.game = new DatingGame(character, data);
|
||||
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.CharactersDatingTotal, 1);
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(QuestCondition.CharactersDatingTotal, 1);
|
||||
|
||||
// Success
|
||||
return this.game;
|
||||
|
||||
@@ -3,6 +3,7 @@ package emu.nebula.game.gacha;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.GameContextModule;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.inventory.ItemAcquireMap;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.inventory.ItemType;
|
||||
@@ -164,6 +165,9 @@ public class GachaModule extends GameContextModule {
|
||||
var log = new GachaHistoryLog(data.getGachaType(), results);
|
||||
player.getGachaManager().addGachaHistory(log);
|
||||
|
||||
// Trigger achievements
|
||||
player.trigger(AchievementCondition.GachaTotal, amount);
|
||||
|
||||
// Complete
|
||||
return new GachaResult(info, change, results);
|
||||
}
|
||||
|
||||
@@ -2,22 +2,26 @@ package emu.nebula.game.infinitytower;
|
||||
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.InfinityTowerLevelDef;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
|
||||
import emu.nebula.game.player.PlayerProgress;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class InfinityTowerManager extends PlayerManager {
|
||||
private InfinityTowerLevelDef levelData;
|
||||
private int levelId;
|
||||
|
||||
private long buildId;
|
||||
|
||||
public InfinityTowerManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
private PlayerProgress getProgress() {
|
||||
return this.getPlayer().getProgress();
|
||||
}
|
||||
|
||||
public int getBountyLevel() {
|
||||
return 0;
|
||||
@@ -59,7 +63,8 @@ public class InfinityTowerManager extends PlayerManager {
|
||||
}
|
||||
|
||||
// Check logs if the player has completed the level already
|
||||
if (this.getPlayer().getProgress().getInfinityArenaLog().containsKey(this.getLevelId())) {
|
||||
int highestLevel = this.getProgress().getInfinityTowerLog().get(this.getLevelData().getTowerId());
|
||||
if (highestLevel >= this.getLevelId()) {
|
||||
return change;
|
||||
}
|
||||
|
||||
@@ -73,7 +78,14 @@ public class InfinityTowerManager extends PlayerManager {
|
||||
change.setExtraData(rewards);
|
||||
|
||||
// Log in player progress
|
||||
this.getPlayer().getProgress().addInfinityArenaLog(this.getLevelId());
|
||||
this.getPlayer().getProgress().addInfinityTowerLog(this.getLevelData());
|
||||
|
||||
// Trigger achievement
|
||||
this.getPlayer().trigger(AchievementCondition.InfinityTowerClearSpecificFloor, 10, this.getLevelId(), 0);
|
||||
|
||||
// Clear instance
|
||||
this.levelId = 0;
|
||||
this.buildId = 0;
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
|
||||
@@ -8,7 +8,7 @@ import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.game.player.PlayerProgress;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import lombok.Getter;
|
||||
@@ -35,7 +35,7 @@ public class InstanceManager extends PlayerManager {
|
||||
return this.getPlayer().getProgress();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settleInstance(InstanceData data, QuestCondType questCondition, Int2IntMap log, String logName, int star) {
|
||||
public PlayerChangeInfo settleInstance(InstanceData data, QuestCondition questCondition, Int2IntMap log, String logName, int star) {
|
||||
// Calculate settle data
|
||||
var settleData = new InstanceSettleData();
|
||||
|
||||
@@ -63,18 +63,22 @@ public class InstanceManager extends PlayerManager {
|
||||
this.getProgress().saveInstanceLog(log, logName, data.getId(), star);
|
||||
|
||||
// Quest triggers
|
||||
this.getPlayer().triggerQuest(questCondition, 1);
|
||||
this.getPlayer().triggerQuest(QuestCondType.BattleTotal, 1);
|
||||
this.getPlayer().trigger(questCondition, 1);
|
||||
this.getPlayer().trigger(QuestCondition.BattleTotal, 1);
|
||||
}
|
||||
|
||||
// Set extra data
|
||||
change.setExtraData(settleData);
|
||||
|
||||
// Clear instance
|
||||
this.curInstanceId = 0;
|
||||
this.rewardType = 0;
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo sweepInstance(InstanceData data, QuestCondType questCondition, Int2IntMap log, int rewardType, int count) {
|
||||
public PlayerChangeInfo sweepInstance(InstanceData data, QuestCondition questCondition, Int2IntMap log, int rewardType, int count) {
|
||||
// Sanity check count
|
||||
if (count <= 0) {
|
||||
return null;
|
||||
@@ -131,8 +135,58 @@ public class InstanceManager extends PlayerManager {
|
||||
change.setExtraData(list);
|
||||
|
||||
// Quest triggers
|
||||
this.getPlayer().triggerQuest(questCondition, count);
|
||||
this.getPlayer().triggerQuest(QuestCondType.BattleTotal, count);
|
||||
this.getPlayer().trigger(questCondition, count);
|
||||
this.getPlayer().trigger(QuestCondition.BattleTotal, count);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settleWeekly(InstanceData data, boolean win) {
|
||||
// Calculate settle data
|
||||
var settleData = new InstanceSettleData();
|
||||
|
||||
settleData.setWin(win);
|
||||
settleData.setFirst(settleData.isWin() && !getProgress().getWeekBossLog().containsKey(data.getId()));
|
||||
|
||||
// Init player change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Handle win
|
||||
if (settleData.isWin()) {
|
||||
// Calculate rewards
|
||||
int entries = this.getPlayer().getInventory().getResourceCount(GameConstants.WEEKLY_ENTRY_ITEM_ID);
|
||||
|
||||
if (entries > 0) {
|
||||
// Generate regular rewards
|
||||
settleData.setRewards(data.getRewards().generate());
|
||||
|
||||
// Add regular rewards
|
||||
getPlayer().getInventory().addItems(settleData.getRewards(), change);
|
||||
|
||||
// Remove weekly entry
|
||||
getPlayer().getInventory().removeItem(GameConstants.WEEKLY_ENTRY_ITEM_ID, 1, change);
|
||||
}
|
||||
|
||||
// Add first clear rewards even if we dont have the entry ticket
|
||||
if (settleData.isFirst()) {
|
||||
// Generate first clear rewards
|
||||
settleData.setRewards(data.getFirstRewards().generate());
|
||||
|
||||
// Add to inventory
|
||||
getPlayer().getInventory().addItems(settleData.getFirstRewards(), change);
|
||||
}
|
||||
|
||||
// Log
|
||||
this.getProgress().saveInstanceLog(getProgress().getWeekBossLog(), "weekBossLog", data.getId(), 1);
|
||||
|
||||
// Quest triggers
|
||||
this.getPlayer().trigger(QuestCondition.WeekBoosClearSpecificDifficultyAndTotal, 1);
|
||||
this.getPlayer().trigger(QuestCondition.BattleTotal, 1);
|
||||
}
|
||||
|
||||
// Set extra data
|
||||
change.setExtraData(settleData);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
|
||||
@@ -17,6 +17,10 @@ public class InstanceSettleData {
|
||||
|
||||
}
|
||||
|
||||
public void generateRewards(InstanceData data) {
|
||||
this.generateRewards(data, 0);
|
||||
}
|
||||
|
||||
public void generateRewards(InstanceData data, int rewardType) {
|
||||
if (this.isFirst) {
|
||||
this.firstRewards = data.getFirstRewards(rewardType).generate();
|
||||
|
||||
@@ -7,11 +7,12 @@ import dev.morphia.annotations.Id;
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.DropPkgDef;
|
||||
import emu.nebula.data.resources.MallShopDef;
|
||||
import emu.nebula.data.resources.ResidentGoodsDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.Notify.Skin;
|
||||
import emu.nebula.proto.Public.Honor;
|
||||
@@ -20,6 +21,7 @@ import emu.nebula.proto.Public.Res;
|
||||
import emu.nebula.proto.Public.Title;
|
||||
import emu.nebula.proto.Public.UI32;
|
||||
import emu.nebula.util.String2IntMap;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@@ -283,8 +285,8 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
change = new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
// Sanity
|
||||
if (count == 0) {
|
||||
// Sanity check
|
||||
if (id <= 0 || count == 0) {
|
||||
return change;
|
||||
}
|
||||
|
||||
@@ -338,11 +340,32 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
case Item -> {
|
||||
// Check if item is a random package
|
||||
if (data.getItemSubType() == ItemSubType.RandomPackage && data.getUseParams() != null) {
|
||||
// Cannot remove packages
|
||||
if (count <= 0) break;
|
||||
|
||||
// Add random packages
|
||||
for (var entry : data.getUseParams()) {
|
||||
int pkgId = entry.getIntKey();
|
||||
int pkgCount = entry.getIntValue() * count;
|
||||
|
||||
for (int i = 0; i < pkgCount; i++) {
|
||||
int pkgDropId = DropPkgDef.getRandomDrop(pkgId);
|
||||
this.addItem(pkgDropId, 1, change);
|
||||
}
|
||||
}
|
||||
|
||||
// End early
|
||||
break;
|
||||
}
|
||||
|
||||
// Get item
|
||||
var item = this.items.get(id);
|
||||
int diff = 0;
|
||||
|
||||
if (amount > 0) {
|
||||
// Add resource
|
||||
// Add item
|
||||
if (item == null) {
|
||||
item = new GameItem(this.getPlayer(), id, amount);
|
||||
this.items.put(item.getItemId(), item);
|
||||
@@ -376,25 +399,51 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
case Disc -> {
|
||||
if (amount <= 0) {
|
||||
// Cannot remove discs
|
||||
if (amount <= 0) break;
|
||||
|
||||
// Get disc data
|
||||
var discData = GameData.getDiscDataTable().get(id);
|
||||
if (discData == null) break;
|
||||
|
||||
// Add transform item instead if we already have this disc
|
||||
if (getPlayer().getCharacters().hasDisc(id)) {
|
||||
this.addItem(discData.getTransformItemId(), amount, change);
|
||||
break;
|
||||
}
|
||||
|
||||
var disc = getPlayer().getCharacters().addDisc(id);
|
||||
// Add disc
|
||||
var disc = getPlayer().getCharacters().addDisc(discData);
|
||||
|
||||
// Add to change info
|
||||
if (disc != null) {
|
||||
change.add(disc.toProto());
|
||||
} else {
|
||||
amount = 0;
|
||||
}
|
||||
}
|
||||
case Char -> {
|
||||
if (amount <= 0) {
|
||||
// Cannot remove characters
|
||||
if (amount <= 0) break;
|
||||
|
||||
// Get character data
|
||||
var charData = GameData.getCharacterDataTable().get(id);
|
||||
if (charData == null) break;
|
||||
|
||||
// Add transform item instead if we already have this character
|
||||
if (getPlayer().getCharacters().hasCharacter(id)) {
|
||||
this.addItem(charData.getFragmentsId(), charData.getTransformQty(), change);
|
||||
break;
|
||||
}
|
||||
|
||||
var character = getPlayer().getCharacters().addCharacter(id);
|
||||
|
||||
// Add character
|
||||
var character = getPlayer().getCharacters().addCharacter(charData);
|
||||
|
||||
// Add to change info
|
||||
if (character != null) {
|
||||
change.add(character.toProto());
|
||||
} else {
|
||||
amount = 0;
|
||||
}
|
||||
}
|
||||
case Energy -> {
|
||||
@@ -462,11 +511,11 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger quest
|
||||
// Trigger quest + achievement
|
||||
if (amount > 0) {
|
||||
this.getPlayer().triggerQuest(QuestCondType.ItemsAdd, amount, id);
|
||||
} else {
|
||||
this.getPlayer().triggerQuest(QuestCondType.ItemsDeplete, Math.abs(amount), id);
|
||||
this.getPlayer().trigger(QuestCondition.ItemsAdd, amount, id);
|
||||
} else if (amount < 0) {
|
||||
this.getPlayer().trigger(QuestCondition.ItemsDeplete, Math.abs(amount), id);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -634,6 +683,9 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
// Add produced items
|
||||
this.addItem(data.getProductionId(), data.getProductionPerBatch() * num, change);
|
||||
|
||||
// Trigger achievement
|
||||
this.getPlayer().trigger(AchievementCondition.ItemsProductTotal, num);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
@@ -821,6 +873,20 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
return change.setSuccess(true);
|
||||
}
|
||||
|
||||
public void resetShopPurchases() {
|
||||
// Clear shop purchases if it's not empty
|
||||
if (!this.getShopBuyCount().isEmpty()) {
|
||||
this.getShopBuyCount().clear();
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "shopBuyCount", this.getShopBuyCount());
|
||||
}
|
||||
|
||||
// Clear mall purchases if it's not empty
|
||||
if (!this.getMallBuyCount().isEmpty()) {
|
||||
this.getMallBuyCount().clear();
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "mallBuyCount", this.getMallBuyCount());
|
||||
}
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
|
||||
@@ -12,6 +12,9 @@ import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.account.Account;
|
||||
import emu.nebula.game.achievement.AchievementCondition;
|
||||
import emu.nebula.game.achievement.AchievementManager;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.agent.AgentManager;
|
||||
import emu.nebula.game.battlepass.BattlePassManager;
|
||||
import emu.nebula.game.character.CharacterStorage;
|
||||
@@ -23,7 +26,7 @@ import emu.nebula.game.infinitytower.InfinityTowerManager;
|
||||
import emu.nebula.game.instance.InstanceManager;
|
||||
import emu.nebula.game.inventory.Inventory;
|
||||
import emu.nebula.game.mail.Mailbox;
|
||||
import emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.game.quest.QuestManager;
|
||||
import emu.nebula.game.scoreboss.ScoreBossManager;
|
||||
import emu.nebula.game.story.StoryManager;
|
||||
@@ -32,6 +35,7 @@ import emu.nebula.game.vampire.VampireSurvivorManager;
|
||||
import emu.nebula.net.GameSession;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.net.NetMsgPacket;
|
||||
import emu.nebula.proto.Notify.SigninRewardUpdate;
|
||||
import emu.nebula.proto.PlayerData.DictionaryEntry;
|
||||
import emu.nebula.proto.PlayerData.DictionaryTab;
|
||||
import emu.nebula.proto.PlayerData.PlayerInfo;
|
||||
@@ -41,7 +45,6 @@ import emu.nebula.proto.Public.Friend;
|
||||
import emu.nebula.proto.Public.HonorInfo;
|
||||
import emu.nebula.proto.Public.NewbieInfo;
|
||||
import emu.nebula.proto.Public.QuestType;
|
||||
import emu.nebula.proto.Public.Story;
|
||||
import emu.nebula.proto.Public.WorldClass;
|
||||
import emu.nebula.proto.Public.WorldClassRewardState;
|
||||
import emu.nebula.util.Utils;
|
||||
@@ -81,6 +84,8 @@ public class Player implements GameDatabaseObject {
|
||||
private int energy;
|
||||
private long energyLastUpdate;
|
||||
|
||||
private int signInIndex;
|
||||
|
||||
private long lastEpochDay;
|
||||
private long lastLogin;
|
||||
private long createTime;
|
||||
@@ -104,7 +109,9 @@ public class Player implements GameDatabaseObject {
|
||||
private transient PlayerProgress progress;
|
||||
private transient StoryManager storyManager;
|
||||
private transient QuestManager questManager;
|
||||
private transient AchievementManager achievementManager;
|
||||
private transient AgentManager agentManager;
|
||||
private transient ActivityManager activityManager;
|
||||
|
||||
// Extra
|
||||
private transient Stack<NetMsgPacket> nextPackages;
|
||||
@@ -180,23 +187,26 @@ public class Player implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
public void setSession(GameSession session) {
|
||||
if (this.session != null) {
|
||||
// Sanity check
|
||||
if (this.session == session) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear player from session
|
||||
this.session.clearPlayer();
|
||||
// Don't set session if it's the same session
|
||||
if (this.session == session) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache previous session
|
||||
var prevSession = this.session;
|
||||
|
||||
// Set session
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public void removeSession() {
|
||||
this.session = null;
|
||||
Nebula.getGameContext().getPlayerModule().removeFromCache(this);
|
||||
|
||||
// Clear player reference from the previous session
|
||||
if (prevSession != null) {
|
||||
prevSession.clearPlayer();
|
||||
}
|
||||
|
||||
// We cleared session, now remove player from cache
|
||||
if (this.session == null) {
|
||||
Nebula.getGameContext().getPlayerModule().removeFromCache(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSession() {
|
||||
@@ -204,8 +214,14 @@ public class Player implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
// Set player world class (level)
|
||||
this.level = level;
|
||||
|
||||
// Save to database
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "level", this.level);
|
||||
|
||||
// Trigger achievement
|
||||
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
||||
}
|
||||
|
||||
public void setExp(int exp) {
|
||||
@@ -215,14 +231,12 @@ public class Player implements GameDatabaseObject {
|
||||
|
||||
public void setRemoteToken(String token) {
|
||||
// Skip if tokens are the same
|
||||
if (this.remoteToken == null) {
|
||||
if (this.getRemoteToken() == null) {
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
} else if (this.remoteToken != null) {
|
||||
if (this.remoteToken.equals(token)) {
|
||||
return;
|
||||
}
|
||||
} else if (this.getRemoteToken().equals(token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set remote token
|
||||
@@ -481,16 +495,17 @@ public class Player implements GameDatabaseObject {
|
||||
|
||||
// Save to database
|
||||
Nebula.getGameDatabase().update(
|
||||
this,
|
||||
this.getUid(),
|
||||
"level",
|
||||
this.getLevel(),
|
||||
"exp",
|
||||
this.getExp()
|
||||
this,
|
||||
this.getUid(),
|
||||
"level",
|
||||
this.getLevel(),
|
||||
"exp",
|
||||
this.getExp()
|
||||
);
|
||||
|
||||
// Save level rewards if we changed it
|
||||
if (oldLevel != this.getLevel()) {
|
||||
// Update level rewards
|
||||
this.getQuestManager().saveLevelRewards();
|
||||
|
||||
this.addNextPackage(
|
||||
@@ -498,6 +513,9 @@ public class Player implements GameDatabaseObject {
|
||||
WorldClassRewardState.newInstance()
|
||||
.setFlag(getQuestManager().getLevelRewards().toBigEndianByteArray())
|
||||
);
|
||||
|
||||
// Trigger achievement
|
||||
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
||||
}
|
||||
|
||||
// Calculate changes
|
||||
@@ -550,7 +568,7 @@ public class Player implements GameDatabaseObject {
|
||||
change = modifyEnergy(-amount, change);
|
||||
|
||||
// Trigger quest
|
||||
this.triggerQuest(QuestCondType.EnergyDeplete, amount);
|
||||
this.trigger(QuestCondition.EnergyDeplete, amount);
|
||||
|
||||
// Complete
|
||||
return change;
|
||||
@@ -590,6 +608,13 @@ public class Player implements GameDatabaseObject {
|
||||
public void checkResetDailies() {
|
||||
// Sanity check to make sure daily reset isnt being triggered wrong
|
||||
if (Nebula.getGameContext().getEpochDays() <= this.getLastEpochDay()) {
|
||||
// Fix sign-in index
|
||||
// TODO remove later
|
||||
if (this.getSignInIndex() <= 0) {
|
||||
this.getSignInRewards(false);
|
||||
}
|
||||
|
||||
// End
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -598,29 +623,102 @@ public class Player implements GameDatabaseObject {
|
||||
int curWeek = Utils.getWeeks(this.getLastEpochDay());
|
||||
boolean hasWeekChanged = Nebula.getGameContext().getEpochWeeks() > curWeek;
|
||||
|
||||
// Check if month was changed
|
||||
int curMonth = Utils.getMonths(this.getLastEpochDay());
|
||||
boolean hasMonthChanged = Nebula.getGameContext().getEpochMonths() > curMonth;
|
||||
|
||||
// Reset dailies
|
||||
this.resetDailies(hasWeekChanged);
|
||||
this.resetDailies(hasWeekChanged, hasMonthChanged);
|
||||
|
||||
// Trigger quest/achievement login
|
||||
this.trigger(QuestCondition.LoginTotal, 1);
|
||||
|
||||
// Give sign-in rewards
|
||||
this.getSignInRewards(hasMonthChanged);
|
||||
|
||||
// Update last epoch day
|
||||
this.lastEpochDay = Nebula.getGameContext().getEpochDays();
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "lastEpochDay", this.lastEpochDay);
|
||||
}
|
||||
|
||||
private void getSignInRewards(boolean resetMonthly) {
|
||||
// Check monthly reset
|
||||
if (resetMonthly) {
|
||||
this.signInIndex = 0;
|
||||
}
|
||||
|
||||
// Get next sign-in index
|
||||
int nextSignIn = this.signInIndex + 1;
|
||||
int group = Utils.getDaysOfMonth(this.getLastEpochDay());
|
||||
|
||||
var data = GameData.getSignInDataTable().get((group << 16) + nextSignIn);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add rewards
|
||||
var change = this.getInventory().addItem(data.getItemId(), data.getItemQty());
|
||||
|
||||
// Add package
|
||||
this.addNextPackage(
|
||||
NetMsgId.signin_reward_change_notify,
|
||||
SigninRewardUpdate.newInstance()
|
||||
.setIndex(nextSignIn)
|
||||
.setSwitch(resetMonthly)
|
||||
.setChange(change.toProto())
|
||||
);
|
||||
|
||||
// Update sign-in index
|
||||
this.signInIndex = nextSignIn;
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "signInIndex", this.signInIndex);
|
||||
}
|
||||
|
||||
public void resetDailies(boolean resetWeekly) {
|
||||
public void resetDailies(boolean resetWeekly, boolean resetMonthly) {
|
||||
// Reset daily quests
|
||||
this.getQuestManager().resetDailyQuests();
|
||||
this.getBattlePassManager().getBattlePass().resetDailyQuests(resetWeekly);
|
||||
|
||||
// Check to reset weeklies
|
||||
if (resetWeekly) {
|
||||
// Add weekly boss entry item
|
||||
int entries = this.getInventory().getResourceCount(GameConstants.WEEKLY_ENTRY_ITEM_ID);
|
||||
if (entries < 3) {
|
||||
this.getInventory().addItem(GameConstants.WEEKLY_ENTRY_ITEM_ID, 3 - entries);
|
||||
}
|
||||
|
||||
// Reset weekly tower tickets
|
||||
this.getProgress().clearWeeklyTowerTicketLog();
|
||||
}
|
||||
|
||||
// Check if we need to reset monthly
|
||||
if (resetMonthly) {
|
||||
// Reset monthly shop purchases
|
||||
this.getInventory().resetShopPurchases();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger quests
|
||||
// Trigger quests + achievements
|
||||
|
||||
public void triggerQuest(QuestCondType condition, int progress) {
|
||||
this.triggerQuest(condition, progress, 0);
|
||||
public void trigger(int condition, int progress, int param1, int param2) {
|
||||
this.getQuestManager().trigger(condition, progress, param1, param2);
|
||||
this.getBattlePassManager().getBattlePass().trigger(condition, progress, param1, param2);
|
||||
this.getAchievementManager().trigger(condition, progress, param1, param2);
|
||||
}
|
||||
|
||||
public void triggerQuest(QuestCondType condition, int progress, int param) {
|
||||
this.getQuestManager().trigger(condition, progress, param);
|
||||
this.getBattlePassManager().getBattlePass().trigger(condition, progress, param);
|
||||
public void trigger(QuestCondition condition, int progress) {
|
||||
this.trigger(condition.getValue(), progress, 0, 0);
|
||||
}
|
||||
|
||||
public void trigger(QuestCondition condition, int progress, int param1) {
|
||||
this.trigger(condition.getValue(), progress, param1, 0);
|
||||
}
|
||||
|
||||
public void trigger(AchievementCondition condition, int progress) {
|
||||
this.trigger(condition.getValue(), progress, 0, 0);
|
||||
}
|
||||
|
||||
public void trigger(AchievementCondition condition, int progress, int param1, int param2) {
|
||||
this.trigger(condition.getValue(), progress, param1, param2);
|
||||
}
|
||||
|
||||
// Login
|
||||
@@ -664,7 +762,9 @@ public class Player implements GameDatabaseObject {
|
||||
this.gachaManager = this.loadManagerFromDatabase(GachaManager.class);
|
||||
this.storyManager = this.loadManagerFromDatabase(StoryManager.class);
|
||||
this.questManager = this.loadManagerFromDatabase(QuestManager.class);
|
||||
this.achievementManager = this.loadManagerFromDatabase(AchievementManager.class);
|
||||
this.agentManager = this.loadManagerFromDatabase(AgentManager.class);
|
||||
this.activityManager = this.loadManagerFromDatabase(ActivityManager.class);
|
||||
|
||||
// Database fixes
|
||||
if (this.showChars == null) {
|
||||
@@ -672,17 +772,22 @@ public class Player implements GameDatabaseObject {
|
||||
this.save();
|
||||
}
|
||||
|
||||
// Init activities
|
||||
this.getActivityManager().init();
|
||||
|
||||
// Load complete
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
public void onCreate() {
|
||||
// Send welcome mail
|
||||
this.getMailbox().sendWelcomeMail();
|
||||
}
|
||||
|
||||
public void onLogin() {
|
||||
// See if we need to reset dailies
|
||||
this.checkResetDailies();
|
||||
|
||||
// Trigger quest login
|
||||
this.triggerQuest(QuestCondType.LoginTotal, 1);
|
||||
|
||||
// Update last login time
|
||||
this.lastLogin = System.currentTimeMillis();
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "lastLogin", this.getLastLogin());
|
||||
@@ -698,11 +803,25 @@ public class Player implements GameDatabaseObject {
|
||||
this.getNextPackages().add(new NetMsgPacket(msgId, proto));
|
||||
}
|
||||
|
||||
// Misc
|
||||
|
||||
/**
|
||||
* Called AFTER a response is sent to the client
|
||||
*/
|
||||
public void afterResponse() {
|
||||
// Check if we need save achievements
|
||||
if (this.getAchievementManager().isQueueSave()) {
|
||||
this.getAchievementManager().save();
|
||||
}
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public PlayerInfo toProto() {
|
||||
PlayerInfo proto = PlayerInfo.newInstance()
|
||||
.setServerTs(Nebula.getCurrentTime())
|
||||
.setSigninIndex(this.getSignInIndex())
|
||||
.setTowerTicket(this.getProgress().getTowerTickets())
|
||||
.setDailyShopRewardStatus(this.getQuestManager().hasDailyReward())
|
||||
.setAchievements(new byte[64]);
|
||||
|
||||
@@ -776,18 +895,20 @@ public class Player implements GameDatabaseObject {
|
||||
|
||||
// Set player states
|
||||
var state = proto.getMutableState()
|
||||
.setStorySet(true)
|
||||
.setStorySet(this.getStoryManager().hasNew())
|
||||
.setFriend(this.getFriendList().hasPendingRequests());
|
||||
|
||||
state.getMutableMail()
|
||||
.setNew(this.getMailbox().hasNewMail());
|
||||
|
||||
state.getMutableBattlePass()
|
||||
.setState(1);
|
||||
.setState(this.getBattlePassManager().hasNew() ? 1 : 0);
|
||||
|
||||
state.getMutableAchievement()
|
||||
.setNew(this.getAchievementManager().hasNewAchievements());
|
||||
|
||||
state.getMutableFriendEnergy();
|
||||
state.getMutableMallPackage();
|
||||
state.getMutableAchievement();
|
||||
state.getMutableTravelerDuelQuest()
|
||||
.setType(QuestType.TravelerDuel);
|
||||
state.getMutableTravelerDuelChallengeQuest()
|
||||
@@ -809,14 +930,7 @@ public class Player implements GameDatabaseObject {
|
||||
acc.addNewbies(NewbieInfo.newInstance().setGroupId(GameConstants.INTRO_GUIDE_ID).setStepId(-1));
|
||||
|
||||
// Story
|
||||
var story = proto.getMutableStory();
|
||||
|
||||
for (int storyId : this.getStoryManager().getCompletedStories()) {
|
||||
var storyProto = Story.newInstance()
|
||||
.setIdx(storyId);
|
||||
|
||||
story.addStories(storyProto);
|
||||
}
|
||||
this.getStoryManager().encodePlayerInfo(proto);
|
||||
|
||||
// Add titles
|
||||
for (int titleId : this.getInventory().getTitles()) {
|
||||
@@ -832,7 +946,7 @@ public class Player implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
// Quests
|
||||
this.getQuestManager().encodeProto(proto);
|
||||
this.getQuestManager().encodePlayerInfo(proto);
|
||||
|
||||
// Add dictionary tabs
|
||||
for (var dictionaryData : GameData.getDictionaryTabDataTable()) {
|
||||
@@ -851,7 +965,7 @@ public class Player implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
// Add progress
|
||||
this.getProgress().encodeProto(proto);
|
||||
this.getProgress().encodePlayerInfo(proto);
|
||||
|
||||
// Handbook
|
||||
proto.addHandbook(this.getCharacters().getCharacterHandbook());
|
||||
@@ -868,6 +982,11 @@ public class Player implements GameDatabaseObject {
|
||||
agentProto.addInfos(agent.toProto());
|
||||
}
|
||||
|
||||
// Activities
|
||||
for (var activity : getActivityManager().getActivities().values()) {
|
||||
proto.addActivities(activity.toProto());
|
||||
}
|
||||
|
||||
// Complete
|
||||
return proto;
|
||||
}
|
||||
|
||||
192
src/main/java/emu/nebula/game/player/PlayerErrorCode.java
Normal file
192
src/main/java/emu/nebula/game/player/PlayerErrorCode.java
Normal file
@@ -0,0 +1,192 @@
|
||||
package emu.nebula.game.player;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
// resources\bin\ErrorCode.json
|
||||
// resources\language\en_US\ErrorCode.json
|
||||
|
||||
@Getter
|
||||
public enum PlayerErrorCode {
|
||||
ErrInvalidToken(100101),
|
||||
ErrInvalidTimestamp(100102),
|
||||
ErrInvalidMsgId(100103),
|
||||
ErrInvalidMsgBody(100104),
|
||||
ErrAgentBusy(100105),
|
||||
ErrServiceUnavailable(100106),
|
||||
ErrRequestTimeout(100107),
|
||||
ErrLoginFailed(100108),
|
||||
ErrInvalidVersion(100109),
|
||||
ErrLogin(100110),
|
||||
ErrMask(110101),
|
||||
ErrServerBusy(110102),
|
||||
ErrData(110103),
|
||||
ErrArgs(110104),
|
||||
ErrInternal(110105),
|
||||
ErrTokenExpire(110106),
|
||||
ErrRelogin(110107),
|
||||
ErrInvalidReq(110108),
|
||||
ErrApiDisabled(110109),
|
||||
ErrResUpdated(110110),
|
||||
ErrOperateTooFast(110111),
|
||||
ErrNickLenViolation(110201),
|
||||
ErrNickIllegal(110202),
|
||||
ErrNickForbidden(110203),
|
||||
ErrBan(110205),
|
||||
ErrSignatureForbidden(110206),
|
||||
ErrSignatureLenViolation(110207),
|
||||
ErrSignatureIllegal(110208),
|
||||
ErrTitleNotFound(110209),
|
||||
ErrSurveyNotFound(110210),
|
||||
ErrSurveyNotOpenYet(110211),
|
||||
ErrSurveyHasCompleted(110212),
|
||||
ErrMailMiss(110401),
|
||||
ErrMailRevoked(110402),
|
||||
ErrMailUpdate(110403),
|
||||
ErrMailPinned(110404),
|
||||
ErrQuestNotComplete(110501),
|
||||
ErrQuestClosed(110502),
|
||||
ErrProductNotReady(110601),
|
||||
ErrProductRestockLimit(110602),
|
||||
ErrGoodsNotFound(110603),
|
||||
ErrGoodsLockPurchase(110604),
|
||||
ErrGoodsDelisted(110605),
|
||||
ErrShopNotFound(110606),
|
||||
ErrShopAlreadyClosed(110607),
|
||||
ErrRecvReward(110701),
|
||||
ErrMainlineCondPremission(110702),
|
||||
ErrEnergyHoldLimit(110801),
|
||||
ErrEnergyBuyLimit(110802),
|
||||
ErrEnergyLack(110803),
|
||||
ErrEnergyRestore(110804),
|
||||
ErrGachaSpinLimit(110901),
|
||||
ErrItemUseRefused(111001),
|
||||
ErrRglInProgress(111101),
|
||||
ErrRglCond(111102),
|
||||
ErrRglBuildNotFound(111103),
|
||||
ErrRglBuildLock(111104),
|
||||
ErrFormationIdNeed(111105),
|
||||
ErrFormationFullNeed(111106),
|
||||
ErrBuildNameMinLenLimit(111107),
|
||||
ErrBuildNameMaxLenLimit(111108),
|
||||
ErrBuildNameViolation(111109),
|
||||
ErrOutfitStrengthenMax(111201),
|
||||
ErrOutfitNoData(111202),
|
||||
ErrOutfitBeEquipped(111203),
|
||||
ErrOutfitBeLocked(111204),
|
||||
ErrOutfitPhaseMax(111205),
|
||||
ErrOutfitStarMax(111206),
|
||||
ErrOutfitLevelNotReached(111207),
|
||||
ErrGiftPackageInvalid(111301),
|
||||
ErrGiftPackageExpired(111302),
|
||||
ErrGiftPackError(111303),
|
||||
ErrRedeemCodeReachedLimit(111304),
|
||||
ErrUserExchangeCountReachedLimit(111305),
|
||||
ErrGiftPackageAlreadyExists(111306),
|
||||
ErrGiftPackageInfoInvalid(111307),
|
||||
ErrRedeemCodeItemListEmpty(111308),
|
||||
ErrRedeemCodeCodeItemInvalid(111309),
|
||||
ErrGiftPackageStartTimeError(111310),
|
||||
ErrGiftPackageEndTimeError(111311),
|
||||
ErrGiftPackageRedeemCodeQuantityExceedsLimit(111312),
|
||||
ErrFixedRedeemCodeAlreadyExists(111313),
|
||||
ErrFixedRedeemCodeCannotBeEmpty(111314),
|
||||
ErrRedeemCodeQuantityCannotBeZero(111315),
|
||||
ErrFixedRedeemCodeUserUsageQuantityRequired(111316),
|
||||
ErrRedeemCodeInvalid(111317),
|
||||
ErrRedeemCodeFailed(111318),
|
||||
ErrRedeemCodeAlreadyUsed(111319),
|
||||
ErrRedeemFailed(111320),
|
||||
ErrInviteNotFound(111401),
|
||||
ErrUserInviteCountLimit(111402),
|
||||
ErrUserFriendCountLimit(111403),
|
||||
ErrAlreadyExistFriend(111404),
|
||||
ErrFriendEnergyReceiveMax(110681),
|
||||
ErrMallPackageNotListed(111501),
|
||||
ErrMallPackageDelisted(111502),
|
||||
ErrMallPackageStockLimit(111503),
|
||||
ErrMallPackageOrderLimit(111504),
|
||||
ErrOrderCantRemove(111505),
|
||||
ErrHeartStoneMax(111601),
|
||||
ErrWeaponMaxAdvance(111602),
|
||||
ErrWeaponCondNoMet(111603),
|
||||
ErrCharMaxLevel(111604),
|
||||
ErrCharCondMaxAdvance(111605),
|
||||
ErrCharMaxAdvance(111606),
|
||||
ErrCharSkillMaxLevel(111607),
|
||||
ErrCharSkillCondNotMet(111608),
|
||||
ErrPresentsNoData(111609),
|
||||
ErrPresentsUpgradeMax(111610),
|
||||
ErrPresentsBeEquipped(111611),
|
||||
ErrPresentsBeLocked(111612),
|
||||
ErrNotAllowedRecruit(111613),
|
||||
ErrAffinityMaxLevel(111614),
|
||||
ErrAffinityGiftSendCountLimit(111615),
|
||||
ErrDatingCountLimit(111616),
|
||||
ErrDatingSendCountLimit(111617),
|
||||
ErrCharWorldClassCondNotMet(111618),
|
||||
ErrDatingCharLimit(111619),
|
||||
ErrCharArchiveRewardReceived(111620),
|
||||
ErrReceiveEnergyFriendNotData(111621),
|
||||
ErrBattlePassVersionMismatch(111701),
|
||||
ErrTravelerDuelSeasonEnd(111801),
|
||||
ErrProductionFormulaUnactivated(111901),
|
||||
ErrTalentAlreayActiveated(112001),
|
||||
ErrTalentGroupNotYetOpen(112002),
|
||||
ErrTalentKeyNodesInsufficientQuantity(112003),
|
||||
ErrTalentOrdinaryNodesInsufficientQuantity(112004),
|
||||
ErrTalentListEmpty(112005),
|
||||
ErrTalentResetFrequently(112006),
|
||||
ErrTalentActivationLimit(112007),
|
||||
ErrTalentNotYetActivated(112008),
|
||||
ErrEquipmentNoData(112101),
|
||||
ErrEquipmentUpgradeMax(112102),
|
||||
ErrEquipmentBeEquipped(112103),
|
||||
ErrEquipmentBeLocked(112104),
|
||||
ErrDiscNoData(112201),
|
||||
ErrDiscLevelNotReached(112202),
|
||||
ErrDiscStrengthenMax(112203),
|
||||
ErrDiscPhaseMax(112204),
|
||||
ErrDiscStarMax(112205),
|
||||
ErrStarTowerRankSeasonNoData(112301),
|
||||
ErrStarTowerRankSeasonNotYetOpen(112302),
|
||||
ErrStarTowerRankSeasonEnd(112303),
|
||||
ErrAgentNoData(112401),
|
||||
ErrAgentInProcess(112402),
|
||||
ErrAgentCond(112403),
|
||||
ErrAgentCharOccupied(112404),
|
||||
ErrAgentBuildOccupied(112405),
|
||||
ErrAgentBuildScoreLow(112406),
|
||||
ErrAgentCountLimit(112407),
|
||||
ErrAgentDailyLimit(112408),
|
||||
ErrAgentWeeklyLimit(112409),
|
||||
ErrAgentNotCompleted(112410),
|
||||
ErrVampireSurvivorBuildNum(112501),
|
||||
ErrVampireSurvivorBuildCharRepetition(112502),
|
||||
ErrTowerGrowthGroupActivated(112601),
|
||||
ErrJointDrillNotExist(112701),
|
||||
ErrJointDrillInProgress(112702),
|
||||
ErrJointDrillNotYetOpen(112703),
|
||||
ErrJointDrillAlreadyClosed(112704),
|
||||
ErrScoreBossNotOpen(112801),
|
||||
ErrNickNameResetLimit(112901),
|
||||
ErrCapOverflow(119901),
|
||||
ErrConfig(119902),
|
||||
ErrCondCheck(119903),
|
||||
ErrNotYetOpen(119904),
|
||||
ErrAlreadyClosed(119905),
|
||||
ErrCondNotMet(119906),
|
||||
ErrInsufficientWorldClass(119907),
|
||||
ErrNoRewardsToReceive(119908),
|
||||
ErrAlreayExists(119909),
|
||||
ErrExchangeNotSupported(119910),
|
||||
ErrRequestTooFrequent(119911),
|
||||
ErrAlreaydReceive(119912),
|
||||
ErrLimit(119913),
|
||||
ErrDataUpdated(119914);
|
||||
|
||||
final int value;
|
||||
|
||||
PlayerErrorCode(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import emu.nebula.Nebula;
|
||||
import emu.nebula.game.GameContext;
|
||||
import emu.nebula.game.GameContextModule;
|
||||
import emu.nebula.game.account.Account;
|
||||
import emu.nebula.game.achievement.AchievementManager;
|
||||
import emu.nebula.game.activity.ActivityManager;
|
||||
import emu.nebula.game.agent.AgentManager;
|
||||
import emu.nebula.game.battlepass.BattlePass;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
@@ -130,12 +132,13 @@ public class PlayerModule extends GameContextModule {
|
||||
player.onLoad();
|
||||
player.save();
|
||||
|
||||
// Send welcome mail
|
||||
player.getMailbox().sendWelcomeMail();
|
||||
// Handle any player creation events
|
||||
player.onCreate();
|
||||
|
||||
// Put in player cache
|
||||
this.addToCache(player);
|
||||
|
||||
// Complete
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -179,6 +182,8 @@ public class PlayerModule extends GameContextModule {
|
||||
datastore.getCollection(StoryManager.class).deleteOne(idFilter);
|
||||
datastore.getCollection(QuestManager.class).deleteOne(idFilter);
|
||||
datastore.getCollection(AgentManager.class).deleteOne(idFilter);
|
||||
datastore.getCollection(AchievementManager.class).deleteOne(idFilter);
|
||||
datastore.getCollection(ActivityManager.class).deleteOne(idFilter);
|
||||
|
||||
datastore.getCollection(BattlePass.class).deleteOne(idFilter);
|
||||
datastore.getCollection(ScoreBossRankEntry.class).deleteOne(idFilter);
|
||||
|
||||
@@ -8,6 +8,7 @@ import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.PostLoad;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.InfinityTowerLevelDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.tutorial.TutorialLevelLog;
|
||||
import emu.nebula.game.vampire.VampireSurvivorLog;
|
||||
@@ -34,6 +35,7 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
// Star Tower
|
||||
private IntSet starTowerLog;
|
||||
private int[] starTowerGrowth;
|
||||
private int towerTickets;
|
||||
|
||||
// Instances
|
||||
private Int2IntMap dailyInstanceLog;
|
||||
@@ -43,7 +45,8 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
private Int2IntMap weekBossLog;
|
||||
|
||||
// Infinite Arena
|
||||
private Int2IntMap infinityArenaLog;
|
||||
private Int2IntMap infinityTowerLog;
|
||||
@Deprecated private Int2IntMap infinityArenaLog;
|
||||
|
||||
// Vampire Survivors
|
||||
private Map<Integer, VampireSurvivorLog> vampireLog;
|
||||
@@ -75,8 +78,8 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
this.charGemLog = new Int2IntOpenHashMap();
|
||||
this.weekBossLog = new Int2IntOpenHashMap();
|
||||
|
||||
// Infinity Arena
|
||||
this.infinityArenaLog = new Int2IntOpenHashMap();
|
||||
// Infinity Tower
|
||||
this.infinityTowerLog = new Int2IntOpenHashMap();
|
||||
|
||||
// Vampire Survivor
|
||||
this.vampireLog = new HashMap<>();
|
||||
@@ -122,17 +125,55 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addInfinityArenaLog(int levelId) {
|
||||
// Calculate arena id
|
||||
int id = (int) Math.floor(levelId / 10000D);
|
||||
/**
|
||||
* Returns the maximum amount of weekly tickets that a player can receive without hitting the limit
|
||||
*/
|
||||
public int getMaxEarnableWeeklyTowerTickets() {
|
||||
return Math.max(this.getWeeklyTowerTicketLimit() - this.getTowerTickets(), 0);
|
||||
}
|
||||
|
||||
public int getWeeklyTowerTicketLimit() {
|
||||
int limit = 2000;
|
||||
|
||||
if (this.getPlayer().getStarTowerManager().hasGrowthNode(10502)) {
|
||||
limit += 1000;
|
||||
} else if (this.getPlayer().getStarTowerManager().hasGrowthNode(10201)) {
|
||||
limit += 500;
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void addWeeklyTowerTicketLog(int count) {
|
||||
this.towerTickets += count;
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "towerTickets", this.towerTickets);
|
||||
}
|
||||
|
||||
public void clearWeeklyTowerTicketLog() {
|
||||
if (this.towerTickets == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.towerTickets = 0;
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "towerTickets", this.towerTickets);
|
||||
}
|
||||
|
||||
public void addInfinityTowerLog(InfinityTowerLevelDef level) {
|
||||
// Calculate tower id
|
||||
int towerId = level.getTowerId();
|
||||
int levelId = level.getId();
|
||||
|
||||
if (towerId <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check highest clear
|
||||
int highestClearId = this.getInfinityArenaLog().get(id);
|
||||
int highestClearId = this.getInfinityTowerLog().get(towerId);
|
||||
|
||||
// Add & Save to database
|
||||
if (levelId > highestClearId) {
|
||||
this.getInfinityArenaLog().put(id, levelId);
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "infinityArenaLog." + id, levelId);
|
||||
this.getInfinityTowerLog().put(towerId, levelId);
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "infinityArenaLog." + towerId, levelId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +193,7 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
|
||||
// Proto
|
||||
|
||||
public void encodeProto(PlayerInfo proto) {
|
||||
public void encodePlayerInfo(PlayerInfo proto) {
|
||||
// Check if we want to unlock all instances
|
||||
boolean unlockAll = Nebula.getConfig().getServerOptions().unlockInstances;
|
||||
|
||||
@@ -261,8 +302,43 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
|
||||
@PostLoad
|
||||
public void postLoad() {
|
||||
boolean shouldSave = false;
|
||||
|
||||
// Fix missing star tower growth
|
||||
if (this.starTowerGrowth == null) {
|
||||
this.starTowerGrowth = new int[1];
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
// Fix missing infinity tower log
|
||||
if (this.infinityTowerLog == null) {
|
||||
this.infinityTowerLog = new Int2IntOpenHashMap();
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
// Carry over infinity tower progress
|
||||
if (this.infinityArenaLog != null) {
|
||||
for (int levelId : this.infinityArenaLog.values()) {
|
||||
var data = GameData.getInfinityTowerLevelDataTable().get(levelId);
|
||||
if (data == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int towerId = data.getTowerId();
|
||||
|
||||
if (towerId > 0) {
|
||||
this.infinityTowerLog.put(data.getTowerId(), levelId);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear old infinity tower logs when done
|
||||
this.infinityArenaLog = null;
|
||||
shouldSave = true;
|
||||
}
|
||||
|
||||
// Update in database if anything changed
|
||||
if (shouldSave) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,19 +55,19 @@ public class GameQuest {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean trigger(QuestCondType condition, int progress, int param) {
|
||||
public boolean trigger(int condition, int progress, int param1, int param2) {
|
||||
// Sanity check
|
||||
if (this.isComplete()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if not the correct condition
|
||||
if (this.cond != condition.getValue()) {
|
||||
if (this.cond != condition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check quest param
|
||||
if (this.param != 0 && param != this.param) {
|
||||
if (this.param != 0 && param1 != this.param) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
144
src/main/java/emu/nebula/game/quest/QuestCondition.java
Normal file
144
src/main/java/emu/nebula/game/quest/QuestCondition.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package emu.nebula.game.quest;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum QuestCondition {
|
||||
BattleTotal (3),
|
||||
BattlesTotalWithPartner (4),
|
||||
CharacterAcquireQuantityRarityAndAdvancement (6),
|
||||
CharacterAdvanceTotal (7),
|
||||
CharacterSkillUpTotal (8),
|
||||
CharacterSkillWithSpecificUpTotal (9),
|
||||
CharacterUpTotal (12),
|
||||
CharacterWithSpecificAdvance (13),
|
||||
CharacterWithSpecificUpLevel (15),
|
||||
CharactersWithSpecificNumberLevelAndAttributes (17),
|
||||
CharactersWithSpecificQuantityAdvancementCountAndAttribute (19),
|
||||
CharactersWithSpecificQuantityRarityAndLevel (22),
|
||||
ChatTotal (23),
|
||||
DailyInstanceClearSpecificDifficultyAndTotal (24),
|
||||
DailyInstanceClearSpecificTypeAndTotal (25),
|
||||
DailyInstanceClearTotal (26),
|
||||
DiscAcquireQuantityLevelAndRarity (30),
|
||||
DiscAcquireQuantityPhaseAndRarity (31),
|
||||
DiscAcquireQuantityStarAndRarity (32),
|
||||
DiscLimitBreakTotal (33),
|
||||
DiscPromoteTotal (34),
|
||||
DiscStrengthenTotal (35),
|
||||
DiscWithSpecificQuantityLevelAndRarity (36),
|
||||
DiscWithSpecificQuantityPhaseAndRarity (37),
|
||||
DiscWithSpecificQuantityStarAndRarity (38),
|
||||
EnergyDeplete (39),
|
||||
GachaTotal (44),
|
||||
GiftGiveTotal (45),
|
||||
InfinityTowerClearSpecificFloor (46),
|
||||
InfinityTowerClearTotal (47),
|
||||
ItemsAdd (48),
|
||||
ItemsDeplete (49),
|
||||
ItemsProductTotal (50),
|
||||
LoginTotal (51),
|
||||
QuestTravelerDuelChallengeTotal (52),
|
||||
QuestTourGuideSpecific (53),
|
||||
QuestTravelerDuelSpecific (54),
|
||||
QuestWithSpecificType (55),
|
||||
RegionBossClearSpecificFullStarWithBossIdAndDifficulty (56),
|
||||
RegionBossClearSpecificLevelWithDifficultyAndTotal (57),
|
||||
RegionBossClearSpecificTotal (58),
|
||||
RegionBossClearTotal (59),
|
||||
SkillsWithSpecificQuantityAndLevel (60),
|
||||
StageClearSpecificStars (62),
|
||||
StoryClear (63),
|
||||
TravelerDuelChallengeSpecificBoosLevelWithDifficultyAndTotal (64),
|
||||
TravelerDuelClearBossTotal (65),
|
||||
TravelerDuelClearSpecificBossIdAndDifficulty (66),
|
||||
TravelerDuelChallengeClearSpecificBossLevelAndAffix (67),
|
||||
TravelerDuelClearSpecificBossLevelWithDifficultyAndTotal (68),
|
||||
TravelerDuelClearSpecificBossTotal (69),
|
||||
TravelerDuelChallengeRankUploadTotal (70),
|
||||
WorldClassSpecific (71),
|
||||
RegionBossClearSpecificTypeWithTotal (72),
|
||||
CharactersWithSpecificDatingCount (73),
|
||||
CharactersDatingTotal (74),
|
||||
VampireSurvivorPassedSpecificLevel (77),
|
||||
CharacterParticipateTowerNumber (78),
|
||||
CharacterAllSkillReachSpecificLevel (79),
|
||||
TravelerDuelPlayTotal (80),
|
||||
VampireClearTotal (81),
|
||||
VampireWithSpecificClearTotal (82),
|
||||
AgentFinishTotal (83),
|
||||
AgentWithSpecificFinishTotal (84),
|
||||
ActivityMiningEnterLayer (86),
|
||||
ActivityMiningDestroyGrid (87),
|
||||
BossRushTotalStars (88),
|
||||
InfinityTowerClearSpecificDifficultyAndTotal (89),
|
||||
SkillInstanceClearTotal (90),
|
||||
VampireSurvivorSpecificPassedLevel (91),
|
||||
WeekBoosClearSpecificDifficultyAndTotal (92),
|
||||
NpcAffinityWithSpecificLevel (93),
|
||||
CharacterPassedWithSpecificTowerAndCount (94),
|
||||
ActivityCookieLevelAccPackage (96),
|
||||
ActivityCookieLevelScore (97),
|
||||
ActivityCookieTypeAccPackage (98),
|
||||
ActivityCookieTypeAccPackCookie (99),
|
||||
ActivityCookieTypeAccRhythm (100),
|
||||
ActivityCookieTypeChallenge (101),
|
||||
CharGemInstanceClearTotal (104),
|
||||
DailyShopReceiveShopTotal (105),
|
||||
AgentApplyTotal (106),
|
||||
ActivityScore (107),
|
||||
ActivityTypeAvgReadWithSpecificIdAndLevelId (108),
|
||||
ActivityTypeLevelPassedWithSpecificIdAndLevelId (109),
|
||||
ActivityTypeLevel3StarPassedWithSpecificIdAndLevelId (110),
|
||||
ActivityTypeLevelStarWithSpecificIdAndLevelTypeTotal (111),
|
||||
ActivityTypeLevelPassedWithSpecificIdAndLevelIdAndSpecificPositionAndCharElem (112),
|
||||
ActivityTypeLevelPassedSpecificIdTotal (113),
|
||||
ClientReport (200),
|
||||
TowerBuildSpecificScoreWithTotal (504),
|
||||
TowerClearSpecificLevelWithDifficultyAndTotal (507),
|
||||
TowerEnterTotal (510),
|
||||
TowerSpecificDifficultyShopBuyTimes (514),
|
||||
TowerGrowthSpecificNote (515),
|
||||
TowerClearSpecificLevelWithDifficultyAndTotalHistory (516),
|
||||
TowerBookWithSpecificEvent (517),
|
||||
TowerBookWithSpecificFateCard (518),
|
||||
TowerBookWithSpecificPotential (520),
|
||||
TowerBuildSpecificDifficultyAndScoreWithTotal (521),
|
||||
TowerSpecificDifficultyStrengthenMachineTotal (522),
|
||||
TowerSpecificDifficultyKillBossTotal (524),
|
||||
TowerBookSpecificCharWithPotentialTotal (525),
|
||||
TowerBuildSpecificCharSpecificScoreWithTotal (526),
|
||||
TowerGrowthWithSpecificNote (527),
|
||||
TowerSpecificFateCardReRollTotal (528),
|
||||
TowerSpecificPotentialReRollTotal (529),
|
||||
TowerSpecificShopReRollTotal (530),
|
||||
TowerSpecificNoteActivateTotal (531),
|
||||
TowerSpecificNoteLevelTotal (532),
|
||||
TowerSpecificPotentialBonusTotal (533),
|
||||
TowerSpecificPotentialLuckyTotal (534),
|
||||
TowerSpecificShopBuyDiscountTotal (535),
|
||||
TowerSpecificSecondarySkillActivateTotal (536),
|
||||
TowerSpecificGetExtraNoteLvTotal (537),
|
||||
TowerEnterFloor (538),
|
||||
TowerSweepTimes (539),
|
||||
TowerSweepTotal (540);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<QuestCondition> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (QuestCondition type : QuestCondition.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private QuestCondition(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static QuestCondition getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
@@ -11,19 +11,20 @@ public class QuestHelper {
|
||||
@Getter
|
||||
private static final Int2ObjectMap<QuestParams> battlePassQuestParams = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
// Put params here
|
||||
static {
|
||||
battlePassQuestParams.put(1001, new QuestParams(QuestCondType.LoginTotal, 1));
|
||||
battlePassQuestParams.put(1002, new QuestParams(QuestCondType.EnergyDeplete, 160));
|
||||
battlePassQuestParams.put(1003, new QuestParams(QuestCondType.BattleTotal, 6));
|
||||
battlePassQuestParams.put(1004, new QuestParams(QuestCondType.QuestWithSpecificType, 5, QuestType.Daily));
|
||||
battlePassQuestParams.put(2001, new QuestParams(QuestCondType.TowerEnterFloor, 1));
|
||||
battlePassQuestParams.put(2002, new QuestParams(QuestCondType.WeekBoosClearSpecificDifficultyAndTotal, 3));
|
||||
battlePassQuestParams.put(2003, new QuestParams(QuestCondType.BattleTotal, 20));
|
||||
battlePassQuestParams.put(2004, new QuestParams(QuestCondType.LoginTotal, 5));
|
||||
battlePassQuestParams.put(2005, new QuestParams(QuestCondType.AgentFinishTotal, 3));
|
||||
battlePassQuestParams.put(2006, new QuestParams(QuestCondType.ItemsDeplete, 100000, 1));
|
||||
battlePassQuestParams.put(2007, new QuestParams(QuestCondType.GiftGiveTotal, 5));
|
||||
battlePassQuestParams.put(2008, new QuestParams(QuestCondType.EnergyDeplete, 1200));
|
||||
battlePassQuestParams.put(1001, new QuestParams(QuestCondition.LoginTotal, 1));
|
||||
battlePassQuestParams.put(1002, new QuestParams(QuestCondition.EnergyDeplete, 160));
|
||||
battlePassQuestParams.put(1003, new QuestParams(QuestCondition.BattleTotal, 6));
|
||||
battlePassQuestParams.put(1004, new QuestParams(QuestCondition.QuestWithSpecificType, 5, QuestType.Daily));
|
||||
battlePassQuestParams.put(2001, new QuestParams(QuestCondition.TowerEnterFloor, 1));
|
||||
battlePassQuestParams.put(2002, new QuestParams(QuestCondition.WeekBoosClearSpecificDifficultyAndTotal, 3));
|
||||
battlePassQuestParams.put(2003, new QuestParams(QuestCondition.BattleTotal, 20));
|
||||
battlePassQuestParams.put(2004, new QuestParams(QuestCondition.LoginTotal, 5));
|
||||
battlePassQuestParams.put(2005, new QuestParams(QuestCondition.AgentFinishTotal, 3));
|
||||
battlePassQuestParams.put(2006, new QuestParams(QuestCondition.ItemsDeplete, 100000, 1));
|
||||
battlePassQuestParams.put(2007, new QuestParams(QuestCondition.GiftGiveTotal, 5));
|
||||
battlePassQuestParams.put(2008, new QuestParams(QuestCondition.EnergyDeplete, 1200));
|
||||
}
|
||||
|
||||
@Getter
|
||||
@@ -40,7 +41,7 @@ public class QuestHelper {
|
||||
this(cond, new int[] {param});
|
||||
}
|
||||
|
||||
public QuestParams(QuestCondType cond, int... params) {
|
||||
public QuestParams(QuestCondition cond, int... params) {
|
||||
this(cond.getValue(), params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,10 +114,10 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
|
||||
this.save();
|
||||
}
|
||||
|
||||
public synchronized void trigger(QuestCondType condition, int progress, int param) {
|
||||
public synchronized void trigger(int condition, int progress, int param1, int param2) {
|
||||
for (var quest : getQuests().values()) {
|
||||
// Try to trigger quest
|
||||
boolean result = quest.trigger(condition, progress, param);
|
||||
boolean result = quest.trigger(condition, progress, param1, param2);
|
||||
|
||||
// Skip if quest progress wasn't changed
|
||||
if (!result) {
|
||||
@@ -132,7 +132,6 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update this quest on the player client
|
||||
*/
|
||||
@@ -194,7 +193,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.QuestWithSpecificType, claimList.size(), QuestType.Daily);
|
||||
this.getPlayer().trigger(QuestCondition.QuestWithSpecificType, claimList.size(), QuestType.Daily);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
@@ -305,7 +304,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "hasDailyReward", this.hasDailyReward);
|
||||
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.DailyShopReceiveShopTotal, 1);
|
||||
this.getPlayer().trigger(QuestCondition.DailyShopReceiveShopTotal, 1);
|
||||
|
||||
// Success
|
||||
return change.setSuccess(true);
|
||||
@@ -313,7 +312,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
|
||||
|
||||
// Serialization
|
||||
|
||||
public void encodeProto(PlayerInfo proto) {
|
||||
public void encodePlayerInfo(PlayerInfo proto) {
|
||||
var quests = proto.getMutableQuests();
|
||||
|
||||
for (var quest : this.getQuests().values()) {
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package emu.nebula.game.scoreboss;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ScoreBossControlDef;
|
||||
import emu.nebula.data.resources.ScoreBossRewardDef;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -20,14 +27,14 @@ public class ScoreBossManager extends PlayerManager {
|
||||
}
|
||||
|
||||
public int getControlId() {
|
||||
return 1;
|
||||
return Nebula.getGameContext().getScoreBossModule().getControlId();
|
||||
}
|
||||
|
||||
public ScoreBossControlDef getControlData() {
|
||||
return GameData.getScoreBossControlDataTable().get(this.getControlId());
|
||||
}
|
||||
|
||||
public ScoreBossRankEntry getRanking() {
|
||||
public ScoreBossRankEntry getRankEntry() {
|
||||
if (this.ranking == null && !this.checkedDatabase) {
|
||||
this.ranking = Nebula.getGameDatabase().getObjectByUid(ScoreBossRankEntry.class, this.getPlayerUid());
|
||||
this.checkedDatabase = true;
|
||||
@@ -57,7 +64,7 @@ public class ScoreBossManager extends PlayerManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean settle(int stars, int score) {
|
||||
public boolean settle(int stars, int score, int skillScore) {
|
||||
// Get level
|
||||
var control = getControlData();
|
||||
if (control == null || !control.getLevelGroup().contains(this.getLevelId())) {
|
||||
@@ -71,7 +78,7 @@ public class ScoreBossManager extends PlayerManager {
|
||||
}
|
||||
|
||||
// Get ranking from database
|
||||
this.getRanking();
|
||||
this.getRankEntry();
|
||||
|
||||
// Create ranking if its not in the database
|
||||
if (this.ranking == null) {
|
||||
@@ -79,7 +86,7 @@ public class ScoreBossManager extends PlayerManager {
|
||||
}
|
||||
|
||||
// Settle
|
||||
this.ranking.settle(this.getPlayer(), build, this.getLevelId(), stars, score);
|
||||
this.ranking.settle(this.getPlayer(), build, getControlId(), getLevelId(), stars, score, skillScore);
|
||||
|
||||
// Save ranking
|
||||
this.ranking.save();
|
||||
@@ -91,4 +98,54 @@ public class ScoreBossManager extends PlayerManager {
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo claimRewards(int star) {
|
||||
// Get rank entry
|
||||
this.getRankEntry();
|
||||
|
||||
if (this.ranking == null) {
|
||||
return new PlayerChangeInfo();
|
||||
}
|
||||
|
||||
// Init variables
|
||||
Collection<ScoreBossRewardDef> claims = null;
|
||||
|
||||
// Add to claim list
|
||||
if (star > 0) {
|
||||
var data = GameData.getScoreBossRewardDataTable().get(star);
|
||||
if (data != null) {
|
||||
claims = List.of(data);
|
||||
}
|
||||
} else {
|
||||
claims = GameData.getScoreBossRewardDataTable().values();
|
||||
}
|
||||
|
||||
// Init rewards
|
||||
var rewards = new ItemParamMap();
|
||||
int starCount = ranking.getStars();
|
||||
boolean shouldSave = false;
|
||||
|
||||
// Try and claim
|
||||
for (var data : claims) {
|
||||
// Check if we have earned enough stars
|
||||
if (starCount >= data.getStarNeed() && !ranking.getClaimedRewards().contains(data.getId())) {
|
||||
// Add rewards
|
||||
rewards.add(data.getRewardItemId1(), data.getRewardNum1());
|
||||
|
||||
// Set in claimed rewards
|
||||
this.ranking.getClaimedRewards().add(data.getId());
|
||||
|
||||
// Set save flag so we update ranking to the database
|
||||
shouldSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Save to database
|
||||
if (shouldSave) {
|
||||
this.ranking.save();
|
||||
}
|
||||
|
||||
// Add rewards
|
||||
return this.getPlayer().getInventory().addItems(rewards);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ public class ScoreBossModule extends GameContextModule {
|
||||
this.ranking = new ArrayList<>();
|
||||
}
|
||||
|
||||
// TODO calculate from bin data
|
||||
public int getControlId() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
private long getRefreshTime() {
|
||||
return Nebula.getConfig().getServerOptions().leaderboardRefreshTime * 1000;
|
||||
}
|
||||
@@ -39,7 +44,7 @@ public class ScoreBossModule extends GameContextModule {
|
||||
this.ranking.clear();
|
||||
|
||||
// Get from database
|
||||
var list = Nebula.getGameDatabase().getSortedObjects(ScoreBossRankEntry.class, "score", 50);
|
||||
var list = Nebula.getGameDatabase().getSortedObjects(ScoreBossRankEntry.class, "controlId", this.getControlId(), "score", 50);
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
// Get rank entry and set proto
|
||||
|
||||
@@ -16,7 +16,8 @@ import emu.nebula.proto.Public.HonorInfo;
|
||||
import emu.nebula.proto.ScoreBossRank.ScoreBossRankChar;
|
||||
import emu.nebula.proto.ScoreBossRank.ScoreBossRankData;
|
||||
import emu.nebula.proto.ScoreBossRank.ScoreBossRankTeam;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -33,6 +34,9 @@ public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||
private int titleSuffix;
|
||||
private int[] honor;
|
||||
private int score;
|
||||
@SuppressWarnings("unused")
|
||||
private int stars;
|
||||
private IntSet claimedRewards;
|
||||
|
||||
private int controlId;
|
||||
private Map<Integer, ScoreBossTeamEntry> teams;
|
||||
@@ -51,6 +55,25 @@ public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||
this.teams = new HashMap<>();
|
||||
}
|
||||
|
||||
// TODO replace later
|
||||
public int getStars() {
|
||||
int stars = 0;
|
||||
|
||||
for (var team : this.getTeams().values()) {
|
||||
stars += team.getStars();
|
||||
}
|
||||
|
||||
return stars;
|
||||
}
|
||||
|
||||
public IntSet getClaimedRewards() {
|
||||
if (this.claimedRewards == null) {
|
||||
this.claimedRewards = new IntOpenHashSet();
|
||||
}
|
||||
|
||||
return this.claimedRewards;
|
||||
}
|
||||
|
||||
public void update(Player player) {
|
||||
this.name = player.getName();
|
||||
this.level = player.getLevel();
|
||||
@@ -60,22 +83,39 @@ public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||
this.honor = player.getHonor();
|
||||
}
|
||||
|
||||
public void settle(Player player, StarTowerBuild build, int level, int stars, int score) {
|
||||
public void settle(Player player, StarTowerBuild build, int controlId, int level, int stars, int score, int skillScore) {
|
||||
// Update player data
|
||||
this.update(player);
|
||||
|
||||
// Reset score entry if control id doesn't match
|
||||
if (this.controlId != controlId) {
|
||||
this.controlId = controlId;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
// Set team entry
|
||||
var team = new ScoreBossTeamEntry(player, build, stars, score);
|
||||
var team = new ScoreBossTeamEntry(player, build, stars, score, skillScore);
|
||||
|
||||
// Put in team map
|
||||
this.getTeams().put(level, team);
|
||||
|
||||
// Calculate score
|
||||
// Calculate score/stars
|
||||
this.score = 0;
|
||||
this.stars = 0;
|
||||
|
||||
for (var t : this.getTeams().values()) {
|
||||
this.score += t.getLevelScore();
|
||||
this.stars += t.getStars();
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
this.score = 0;
|
||||
this.stars = 0;
|
||||
this.getClaimedRewards().clear();
|
||||
this.getTeams().clear();
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public ScoreBossRankData toProto() {
|
||||
@@ -109,6 +149,7 @@ public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||
private int buildScore;
|
||||
private int stars;
|
||||
private int levelScore;
|
||||
private int skillScore;
|
||||
private List<ScoreBossCharEntry> characters;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
@@ -116,11 +157,12 @@ public class ScoreBossRankEntry implements GameDatabaseObject {
|
||||
|
||||
}
|
||||
|
||||
public ScoreBossTeamEntry(Player player, StarTowerBuild build, int stars, int score) {
|
||||
public ScoreBossTeamEntry(Player player, StarTowerBuild build, int stars, int score, int skillScore) {
|
||||
this.buildId = build.getUid();
|
||||
this.buildScore = build.getScore();
|
||||
this.stars = stars;
|
||||
this.levelScore = score;
|
||||
this.skillScore = skillScore;
|
||||
this.characters = new ArrayList<>();
|
||||
|
||||
for (var charId : build.getCharIds()) {
|
||||
|
||||
32
src/main/java/emu/nebula/game/story/StoryChoiceInfo.java
Normal file
32
src/main/java/emu/nebula/game/story/StoryChoiceInfo.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package emu.nebula.game.story;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.nebula.proto.Public.StoryChoice;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class StoryChoiceInfo {
|
||||
private int group;
|
||||
private int value;
|
||||
|
||||
@Deprecated
|
||||
public StoryChoiceInfo() {
|
||||
// Morphia only
|
||||
}
|
||||
|
||||
public StoryChoiceInfo(int group, int value) {
|
||||
this.group = group;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public StoryChoice toProto() {
|
||||
var proto = StoryChoice.newInstance()
|
||||
.setGroup(this.getGroup())
|
||||
.setValue(this.getValue());
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,28 @@
|
||||
package emu.nebula.game.story;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.PostLoad;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.player.PlayerManager;
|
||||
import emu.nebula.proto.PlayerData.PlayerInfo;
|
||||
import emu.nebula.proto.Public.Story;
|
||||
import emu.nebula.proto.StorySett.StorySettle;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
import lombok.Getter;
|
||||
import us.hebi.quickbuf.RepeatedInt;
|
||||
import us.hebi.quickbuf.RepeatedMessage;
|
||||
|
||||
@Getter
|
||||
@Entity(value = "story", useDiscriminator = false)
|
||||
@@ -24,6 +32,13 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
|
||||
private IntSet completedStories;
|
||||
private Int2IntMap completedSets;
|
||||
private IntSet evidences;
|
||||
|
||||
// Note: Story options are seperate from regular story ids to save database space, since most stories do not have options
|
||||
private Map<Integer, StoryOptionLog> options;
|
||||
|
||||
// Current story
|
||||
private transient int storyId;
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public StoryManager() {
|
||||
@@ -35,19 +50,44 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
this.uid = player.getUid();
|
||||
this.completedStories = new IntOpenHashSet();
|
||||
this.completedSets = new Int2IntOpenHashMap();
|
||||
this.evidences = new IntOpenHashSet();
|
||||
this.options = new HashMap<>();
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settle(IntList list) {
|
||||
// Player change info
|
||||
var changes = new PlayerChangeInfo();
|
||||
|
||||
public void apply(int idx) {
|
||||
this.storyId = idx;
|
||||
}
|
||||
|
||||
public boolean hasNew() {
|
||||
if (this.getCompletedStories().size() < GameData.getStoryDataTable().size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int id : list) {
|
||||
if (this.getCompletedSets().size() < GameData.getStorySetSectionDataTable().size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settle(RepeatedMessage<StorySettle> list, RepeatedInt evidences) {
|
||||
// Player change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Handle regular story
|
||||
for (var settle : list) {
|
||||
// Get id
|
||||
int id = settle.getIdx();
|
||||
|
||||
// Get story data
|
||||
var data = GameData.getStoryDataTable().get(id);
|
||||
if (data == null) continue;
|
||||
|
||||
// Settle options (Must be before the completion check as we need to do the same story multiple times to get all the endings)
|
||||
this.settleOptions(settle);
|
||||
|
||||
// Check if we already completed the story
|
||||
if (this.getCompletedStories().contains(id)) {
|
||||
continue;
|
||||
@@ -57,13 +97,64 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
this.getCompletedStories().add(id);
|
||||
|
||||
// Add rewards
|
||||
this.getPlayer().getInventory().addItems(data.getRewards(), changes);
|
||||
this.getPlayer().getInventory().addItems(data.getRewards(), change);
|
||||
|
||||
// Save to db
|
||||
Nebula.getGameDatabase().addToSet(this, this.getPlayerUid(), "completedStories", id);
|
||||
}
|
||||
|
||||
return changes;
|
||||
// Handle evidences
|
||||
for (int id : evidences) {
|
||||
// Verify that evidence id exists
|
||||
var data = GameData.getStoryEvidenceDataTable().get(id);
|
||||
if (data == null) continue;
|
||||
|
||||
// Sanity check
|
||||
if (this.getEvidences().contains(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save to db
|
||||
Nebula.getGameDatabase().addToSet(this, this.getPlayerUid(), "evidences", id);
|
||||
}
|
||||
|
||||
// Clear current story
|
||||
this.storyId = 0;
|
||||
|
||||
// Complete
|
||||
return change;
|
||||
}
|
||||
|
||||
private void settleOptions(StorySettle settle) {
|
||||
// Init variables
|
||||
boolean changed = false;
|
||||
StoryOptionLog log = null;
|
||||
|
||||
// Update
|
||||
if (settle.hasMajor()) {
|
||||
if (log == null) {
|
||||
log = getOptions().computeIfAbsent(settle.getIdx(), idx -> new StoryOptionLog());
|
||||
}
|
||||
|
||||
if (log.settleMajor(settle.getMajor())) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (settle.hasPersonality()) {
|
||||
if (log == null) {
|
||||
log = getOptions().computeIfAbsent(settle.getIdx(), idx -> new StoryOptionLog());
|
||||
}
|
||||
|
||||
if (log.settlePersonality(settle.getPersonality())) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Save to database if we changed anything
|
||||
if (changed) {
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "options." + settle.getIdx(), log);
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settleSet(int chapterId, int sectionId) {
|
||||
@@ -90,6 +181,50 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
// Save to db
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "completedSets." + chapterId, sectionIndex);
|
||||
|
||||
// Complete
|
||||
return changes;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public void encodePlayerInfo(PlayerInfo proto) {
|
||||
var story = proto.getMutableStory();
|
||||
|
||||
for (int storyId : this.getCompletedStories()) {
|
||||
var storyProto = Story.newInstance()
|
||||
.setIdx(storyId);
|
||||
|
||||
var storyOptions = this.getOptions().get(storyId);
|
||||
if (storyOptions != null) {
|
||||
storyOptions.encodeStoryProto(storyProto);
|
||||
}
|
||||
|
||||
story.addStories(storyProto);
|
||||
}
|
||||
|
||||
for (int id : this.getEvidences()) {
|
||||
story.addEvidences(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Database fixes
|
||||
|
||||
@PostLoad
|
||||
public void onLoad() {
|
||||
boolean save = false;
|
||||
|
||||
if (this.evidences == null) {
|
||||
this.evidences = new IntOpenHashSet();
|
||||
save = true;
|
||||
}
|
||||
|
||||
if (this.options == null) {
|
||||
this.options = new HashMap<>();
|
||||
save = true;
|
||||
}
|
||||
|
||||
if (save) {
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
src/main/java/emu/nebula/game/story/StoryOptionLog.java
Normal file
143
src/main/java/emu/nebula/game/story/StoryOptionLog.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package emu.nebula.game.story;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
|
||||
import emu.nebula.proto.Public.Story;
|
||||
import emu.nebula.proto.StorySett.StoryOptions;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import us.hebi.quickbuf.RepeatedMessage;
|
||||
|
||||
@Getter
|
||||
@Entity(useDiscriminator = false)
|
||||
public class StoryOptionLog {
|
||||
private List<StoryChoiceInfo> major;
|
||||
private List<StoryChoiceInfo> personality;
|
||||
|
||||
public StoryOptionLog() {
|
||||
|
||||
}
|
||||
|
||||
public int getMajorOptionSize() {
|
||||
if (this.major == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.major.size();
|
||||
}
|
||||
|
||||
public boolean hasMajorOption(int group, int choice) {
|
||||
if (this.major == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.major.stream()
|
||||
.filter(c -> c.getGroup() == group && c.getValue() == choice)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public boolean addMajorOption(int group, int choice) {
|
||||
if (this.major == null) {
|
||||
this.major = new ArrayList<>();
|
||||
}
|
||||
|
||||
return this.major.add(new StoryChoiceInfo(group, choice));
|
||||
}
|
||||
|
||||
public boolean settleMajor(RepeatedMessage<StoryOptions> options) {
|
||||
boolean success = false;
|
||||
|
||||
for (var option : options) {
|
||||
// Sanity check
|
||||
if (this.getMajorOptionSize() >= 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip if we already have this choice
|
||||
if (this.hasMajorOption(option.getGroup(), option.getChoice())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add
|
||||
this.addMajorOption(option.getGroup(), option.getChoice());
|
||||
|
||||
// Set success flag
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public int getPersonalityOptionSize() {
|
||||
if (this.personality == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.personality.size();
|
||||
}
|
||||
|
||||
public boolean hasPersonalityOption(int group, int choice) {
|
||||
if (this.personality == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.personality.stream()
|
||||
.filter(c -> c.getGroup() == group && c.getValue() == choice)
|
||||
.findFirst()
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public boolean addPersonalityOption(int group, int choice) {
|
||||
if (this.personality == null) {
|
||||
this.personality = new ArrayList<>();
|
||||
}
|
||||
|
||||
return this.personality.add(new StoryChoiceInfo(group, choice));
|
||||
}
|
||||
|
||||
public boolean settlePersonality(RepeatedMessage<StoryOptions> options) {
|
||||
boolean success = false;
|
||||
|
||||
for (var option : options) {
|
||||
// Sanity check
|
||||
if (this.getPersonalityOptionSize() >= 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip if we already have this choice
|
||||
if (this.hasPersonalityOption(option.getGroup(), option.getChoice())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add
|
||||
this.addPersonalityOption(option.getGroup(), option.getChoice());
|
||||
|
||||
// Set success flag
|
||||
success = true;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public void encodeStoryProto(Story proto) {
|
||||
if (this.major != null) {
|
||||
for (var choice : this.major) {
|
||||
proto.addMajor(choice.toProto());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.personality != null) {
|
||||
for (var choice : this.personality) {
|
||||
proto.addMajor(choice.toProto());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.SecondarySkillDef;
|
||||
import emu.nebula.database.GameDatabaseObject;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.character.GameDisc;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.proto.Public.ItemTpl;
|
||||
import emu.nebula.proto.PublicStarTower.BuildPotential;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerBuildBrief;
|
||||
@@ -15,6 +21,7 @@ import emu.nebula.proto.PublicStarTower.StarTowerBuildInfo;
|
||||
import emu.nebula.proto.PublicStarTower.TowerBuildChar;
|
||||
import emu.nebula.util.Snowflake;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@@ -37,55 +44,62 @@ public class StarTowerBuild implements GameDatabaseObject {
|
||||
private ItemParamMap potentials;
|
||||
private ItemParamMap subNoteSkills;
|
||||
|
||||
private IntSet secondarySkills;
|
||||
|
||||
@Deprecated
|
||||
public StarTowerBuild() {
|
||||
|
||||
}
|
||||
|
||||
public StarTowerBuild(StarTowerGame game) {
|
||||
public StarTowerBuild(Player player) {
|
||||
this.uid = Snowflake.newUid();
|
||||
this.playerUid = game.getPlayer().getUid();
|
||||
this.playerUid = player.getUid();
|
||||
this.name = "";
|
||||
this.charPots = new ItemParamMap();
|
||||
this.potentials = new ItemParamMap();
|
||||
this.subNoteSkills = new ItemParamMap();
|
||||
}
|
||||
|
||||
public StarTowerBuild(StarTowerGame game) {
|
||||
// Initialize basic variables
|
||||
this(game.getPlayer());
|
||||
|
||||
// Characters
|
||||
this.charIds = game.getChars().stream()
|
||||
.filter(d -> d.getId() > 0)
|
||||
.mapToInt(d -> d.getId())
|
||||
.toArray();
|
||||
|
||||
// Discs
|
||||
this.discIds = game.getDiscs().stream()
|
||||
.filter(d -> d.getId() > 0)
|
||||
.mapToInt(d -> d.getId())
|
||||
.toArray();
|
||||
// Set char/disc ids
|
||||
this.charIds = game.getCharIds();
|
||||
this.discIds = game.getDiscIds();
|
||||
|
||||
// Add potentials
|
||||
for (var entry : game.getPotentials()) {
|
||||
//
|
||||
// Get potential data
|
||||
int id = entry.getIntKey();
|
||||
int level = entry.getIntValue();
|
||||
|
||||
// Add to potential map
|
||||
this.getPotentials().put(id, level);
|
||||
|
||||
// Add to character
|
||||
var potentialData = GameData.getPotentialDataTable().get(id);
|
||||
if (potentialData != null) {
|
||||
int charId = potentialData.getCharId();
|
||||
this.getCharPots().put(charId, this.getCharPots().get(charId) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Add sub note skills
|
||||
for (var entry : game.getItems()) {
|
||||
this.getSubNoteSkills().put(entry.getIntKey(), entry.getIntValue());
|
||||
this.getSubNoteSkills().add(entry.getIntKey(), entry.getIntValue());
|
||||
}
|
||||
|
||||
// Set secondary skills
|
||||
this.secondarySkills = game.getSecondarySkills();
|
||||
|
||||
// Caclulate record score and cache it
|
||||
this.score = this.calculateScore();
|
||||
this.calculateScore();
|
||||
}
|
||||
|
||||
public void setChars(List<GameCharacter> characters) {
|
||||
this.charIds = characters.stream()
|
||||
.mapToInt(c -> c.getCharId())
|
||||
.toArray();
|
||||
}
|
||||
|
||||
public void setDiscs(List<GameDisc> discs) {
|
||||
this.discIds = discs.stream()
|
||||
.mapToInt(d -> d.getDiscId())
|
||||
.toArray();
|
||||
}
|
||||
|
||||
public void setName(String newName) {
|
||||
@@ -109,26 +123,43 @@ public class StarTowerBuild implements GameDatabaseObject {
|
||||
|
||||
// Score
|
||||
|
||||
private int calculateScore() {
|
||||
// Init score
|
||||
int score = 0;
|
||||
public int calculateScore() {
|
||||
// Clear
|
||||
this.score = 0;
|
||||
this.getCharPots().clear();
|
||||
|
||||
// Potentials
|
||||
for (var potential : this.getPotentials().int2IntEntrySet()) {
|
||||
// Add score from potentials
|
||||
for (var potential : this.getPotentials()) {
|
||||
var data = GameData.getPotentialDataTable().get(potential.getIntKey());
|
||||
if (data == null) continue;
|
||||
|
||||
int index = potential.getIntValue() - 1;
|
||||
score += data.getBuildScore()[index];
|
||||
// Add score
|
||||
this.score += data.getBuildScore(potential.getIntValue());
|
||||
|
||||
// Add for character potential count
|
||||
this.getCharPots().add(data.getCharId(), potential.getIntValue());
|
||||
}
|
||||
|
||||
// Sub note skills
|
||||
// Add score from sub note skills
|
||||
for (var item : this.getSubNoteSkills()) {
|
||||
score += item.getIntValue() * 15;
|
||||
this.score += item.getIntValue() * 15;
|
||||
}
|
||||
|
||||
// Check secondary skills
|
||||
if (this.getSecondarySkills() == null) {
|
||||
this.secondarySkills = SecondarySkillDef.calculateSecondarySkills(this.getDiscIds(), this.getSubNoteSkills());
|
||||
}
|
||||
|
||||
// Add score from secondary skills
|
||||
for (int id : this.getSecondarySkills()) {
|
||||
var data = GameData.getSecondarySkillDataTable().get(id);
|
||||
if (data == null) continue;
|
||||
|
||||
this.score += data.getScore();
|
||||
}
|
||||
|
||||
// Complete
|
||||
return score;
|
||||
return this.score;
|
||||
}
|
||||
|
||||
// Proto
|
||||
@@ -183,6 +214,11 @@ public class StarTowerBuild implements GameDatabaseObject {
|
||||
proto.addSubNoteSkills(skill);
|
||||
}
|
||||
|
||||
// Secondary skills
|
||||
for (int id : this.getSecondarySkills()) {
|
||||
proto.addActiveSecondaryIds(id);
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.HawkerGoods;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class StarTowerCase {
|
||||
private int id;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private CaseType type;
|
||||
|
||||
// Extra data
|
||||
private int teamLevel;
|
||||
private int subNoteSkillNum;
|
||||
|
||||
private int floorId;
|
||||
private int roomType;
|
||||
|
||||
private int eventId;
|
||||
private int npcId;
|
||||
|
||||
// Selector
|
||||
private IntList ids;
|
||||
|
||||
// Hawker
|
||||
private Map<Integer, StarTowerShopGoods> goodsList;
|
||||
|
||||
public StarTowerCase(CaseType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void addId(int id) {
|
||||
if (this.ids == null) {
|
||||
this.ids = new IntArrayList();
|
||||
}
|
||||
|
||||
this.ids.add(id);
|
||||
}
|
||||
|
||||
public int selectId(int index) {
|
||||
if (this.getIds() == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= this.getIds().size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.getIds().getInt(index);
|
||||
}
|
||||
|
||||
public void addGoods(StarTowerShopGoods goods) {
|
||||
if (this.goodsList == null) {
|
||||
this.goodsList = new HashMap<>();
|
||||
}
|
||||
|
||||
this.getGoodsList().put(getGoodsList().size() + 1, goods);
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public StarTowerRoomCase toProto() {
|
||||
var proto = StarTowerRoomCase.newInstance()
|
||||
.setId(this.getId());
|
||||
|
||||
switch (this.type) {
|
||||
case Battle -> {
|
||||
proto.getMutableBattleCase()
|
||||
.setSubNoteSkillNum(this.getSubNoteSkillNum());
|
||||
}
|
||||
case OpenDoor -> {
|
||||
proto.getMutableDoorCase()
|
||||
.setFloor(this.getFloorId())
|
||||
.setType(this.getRoomType());
|
||||
}
|
||||
case SyncHP, RecoveryHP -> {
|
||||
proto.getMutableSyncHPCase();
|
||||
}
|
||||
case SelectSpecialPotential -> {
|
||||
proto.getMutableSelectSpecialPotentialCase()
|
||||
.setTeamLevel(this.getTeamLevel())
|
||||
.addAllIds(this.getIds().toIntArray());
|
||||
}
|
||||
case PotentialSelect -> {
|
||||
proto.getMutableSelectPotentialCase();
|
||||
}
|
||||
case NpcEvent -> {
|
||||
proto.getMutableSelectOptionsEventCase()
|
||||
.setEvtId(this.getEventId())
|
||||
.setNPCId(this.getNpcId())
|
||||
.addAllOptions(this.getIds().toIntArray());
|
||||
}
|
||||
case Hawker -> {
|
||||
var hawker = proto.getMutableHawkerCase();
|
||||
|
||||
for (var entry : getGoodsList().entrySet()) {
|
||||
var sid = entry.getKey();
|
||||
var goods = entry.getValue();
|
||||
|
||||
var info = HawkerGoods.newInstance()
|
||||
.setIdx(goods.getGoodsId())
|
||||
.setSid(sid)
|
||||
.setType(goods.getType())
|
||||
.setGoodsId(102) // ?
|
||||
.setPrice(goods.getPrice())
|
||||
.setTag(1);
|
||||
|
||||
hawker.addList(info);
|
||||
}
|
||||
}
|
||||
default -> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user