mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-12 20:34:36 +01:00
Compare commits
79 Commits
v1.0.0
...
cf63bc0b7e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
898e8dd43f | ||
|
|
29db60fd0a | ||
|
|
7577bf87d4 | ||
|
|
3e47c8ef06 | ||
|
|
a981c6fce4 | ||
|
|
26ea746368 | ||
|
|
4356b194df | ||
|
|
bfb22eddff | ||
|
|
3f2f58b227 | ||
|
|
187d715866 | ||
|
|
7df1be99ef | ||
|
|
6d9e28963d | ||
|
|
2acd506245 | ||
|
|
fd8e8925ca | ||
|
|
e529e8e965 | ||
|
|
3a7d12c7b7 | ||
|
|
008cd06b32 | ||
|
|
55ff9b2826 | ||
|
|
c78c6a7492 | ||
|
|
139b1cf87e | ||
|
|
3059c549d5 | ||
|
|
e8a3576d8b | ||
|
|
a530166347 | ||
|
|
0ef1231bf1 | ||
|
|
e5fe89ebc9 | ||
|
|
be842e4269 | ||
|
|
eead51d32f | ||
|
|
5382877a83 | ||
|
|
fdd4264b40 | ||
|
|
e25d58d7f5 | ||
|
|
f128b93210 | ||
|
|
6289e3f1b6 | ||
|
|
480d0d60b3 | ||
|
|
e4b7bb74c5 | ||
|
|
01ed3da162 |
33
.github/workflows/gradle.yml
vendored
Normal file
33
.github/workflows/gradle.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Nebula Nightly
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
./gradlew jar
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Nebula-Nightly
|
||||
path: Nebula.jar
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -58,12 +58,13 @@ tmp/
|
||||
.DS_Store
|
||||
.directory
|
||||
|
||||
# Generated/resource/log/plugins folders
|
||||
# Generated/resource/log/plugins/etc folders
|
||||
/logs
|
||||
/plugins
|
||||
/proto
|
||||
/resources
|
||||
/web
|
||||
/StellaSoraData
|
||||
|
||||
# Compiled
|
||||
/*.jar
|
||||
|
||||
43
README.md
43
README.md
@@ -6,7 +6,7 @@ For any extra support, questions, or discussions, check out our [Discord](https:
|
||||
|
||||
### Notable features
|
||||
- Basic profile features
|
||||
- Character system implemented (except for affinity)
|
||||
- Character system
|
||||
- Inventory/Discs working
|
||||
- Energy system
|
||||
- Mail system
|
||||
@@ -15,20 +15,28 @@ 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 (missing advanced affinity related features)
|
||||
- Heartlink
|
||||
- Achievements
|
||||
- Monoliths (completeable but many other features missing)
|
||||
- Bounty Trials
|
||||
- Menance Arena
|
||||
- Proving grounds
|
||||
- Catacylsm Survivor (talents not fully working, score not calculated properly)
|
||||
- Catacylsm Survivor (talents not fully working)
|
||||
- Boss Blitz
|
||||
|
||||
### 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`
|
||||
|
||||
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
|
||||
|
||||
### Prerequisites
|
||||
@@ -49,34 +57,43 @@ 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"
|
||||
];
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
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 name, the code field is ignored by the server and can be set to anything.
|
||||
5. Login with your account email, the code field is ignored by the server and can be set to anything.
|
||||
|
||||
### 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.
|
||||
!char [all | {characterId}] lv(level) a(ascension) s(skill level) t(talent level) = Changes the properties of the targeted characters.
|
||||
!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.
|
||||
!level (level) = Sets the player level
|
||||
!mail = Sends the targeted player a system mail.
|
||||
!reload = Reloads the server config.
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
version = '1.0.0'
|
||||
version = '1.1.2'
|
||||
|
||||
var shouldGenerateProto = System.getenv("GENERATE_PROTO") == "true"
|
||||
System.out.println(shouldGenerateProto ? "Generating proto files" : "Skipping proto generation")
|
||||
|
||||
@@ -17,12 +17,15 @@ public class Config {
|
||||
|
||||
public HttpServerConfig httpServer = new HttpServerConfig(80);
|
||||
public GameServerConfig gameServer = new GameServerConfig(80);
|
||||
|
||||
|
||||
public ServerOptions serverOptions = new ServerOptions();
|
||||
public ServerRates serverRates = new ServerRates();
|
||||
public LogOptions logOptions = new LogOptions();
|
||||
public RemoteCommand remoteCommand = new RemoteCommand();
|
||||
|
||||
public int customDataVersion = 0;
|
||||
public String region = "global";
|
||||
|
||||
public String resourceDir = "./resources";
|
||||
public String webFilesDir = "./web";
|
||||
public String patchListPath = "./patchlist.json";
|
||||
@@ -54,35 +57,35 @@ public class Config {
|
||||
public int bindPort;
|
||||
public String publicAddress = "127.0.0.1"; // Will return bindAddress if publicAddress is null
|
||||
public Integer publicPort; // Will return bindPort if publicPort is null
|
||||
|
||||
|
||||
public ServerConfig(int port) {
|
||||
this.bindPort = port;
|
||||
}
|
||||
|
||||
|
||||
public String getPublicAddress() {
|
||||
if (this.publicAddress != null && !this.publicAddress.isEmpty()) {
|
||||
return this.publicAddress;
|
||||
}
|
||||
|
||||
|
||||
return this.bindAddress;
|
||||
}
|
||||
|
||||
|
||||
public int getPublicPort() {
|
||||
if (this.publicPort != null && this.publicPort != 0) {
|
||||
return this.publicPort;
|
||||
}
|
||||
|
||||
|
||||
return this.bindPort;
|
||||
}
|
||||
|
||||
|
||||
public String getDisplayAddress() {
|
||||
return (useSSL ? "https" : "http") + "://" + getPublicAddress() + ":" + getPublicPort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public static class HttpServerConfig extends ServerConfig {
|
||||
|
||||
|
||||
public HttpServerConfig(int port) {
|
||||
super(port);
|
||||
}
|
||||
@@ -90,52 +93,59 @@ public class Config {
|
||||
|
||||
@Getter
|
||||
public static class GameServerConfig extends ServerConfig {
|
||||
|
||||
|
||||
public GameServerConfig(int port) {
|
||||
super(port);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public static class ServerOptions {
|
||||
public Set<String> defaultPermissions = Set.of("*");
|
||||
public boolean autoCreateAccount = true;
|
||||
public boolean skipIntro = false;
|
||||
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
|
||||
public int dailyResetHour = 0;
|
||||
public int leaderboardRefreshTime = 60; // Leaderboard refresh time in seconds
|
||||
public WelcomeMail welcomeMail = new WelcomeMail();
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public static class ServerRates {
|
||||
public double exp = 1.0;
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public static class LogOptions {
|
||||
public boolean commands = true;
|
||||
public boolean packets = false;
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
public static class RemoteCommand {
|
||||
public boolean useRemoteServices = false;
|
||||
public String serverAdminKey = "HJHASDPIIQWEASDHHAN";
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class WelcomeMail {
|
||||
public String title;
|
||||
public String sender;
|
||||
public String content;
|
||||
public List<ItemParam> attachments;
|
||||
|
||||
|
||||
public WelcomeMail() {
|
||||
this.title = "Welcome to a Nebula server";
|
||||
this.sender = "Server";
|
||||
this.content = "Welcome to Nebula! Please take these items as a starter gift.";
|
||||
this.attachments = List.of(
|
||||
new ItemParam(86009, 1),
|
||||
new ItemParam(86002, 1),
|
||||
new ItemParam(1, 1_000_000),
|
||||
new ItemParam(2, 30_000)
|
||||
);
|
||||
new ItemParam(86009, 1),
|
||||
new ItemParam(86002, 1),
|
||||
new ItemParam(1, 1_000_000),
|
||||
new ItemParam(2, 30_000));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,23 @@ import emu.nebula.game.inventory.ItemParam;
|
||||
import emu.nebula.util.WeightedList;
|
||||
|
||||
public class GameConstants {
|
||||
private static final int DATA_VERSION = 51;
|
||||
private static final String VERSION = "1.2.0";
|
||||
public static final String VERSION = "1.2.0";
|
||||
public static int DATA_VERSION = 0;
|
||||
|
||||
// Set data versions for each region
|
||||
static {
|
||||
RegionConfig.getRegion("global")
|
||||
.setDataVersion(63);
|
||||
|
||||
RegionConfig.getRegion("kr")
|
||||
.setDataVersion(70);
|
||||
|
||||
RegionConfig.getRegion("jp")
|
||||
.setDataVersion(66);
|
||||
|
||||
RegionConfig.getRegion("tw")
|
||||
.setDataVersion(64);
|
||||
}
|
||||
|
||||
public static final ZoneId UTC_ZONE = ZoneId.of("UTC");
|
||||
|
||||
@@ -19,7 +34,6 @@ 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 MAX_ENERGY = 240;
|
||||
@@ -29,7 +43,7 @@ public class GameConstants {
|
||||
public static final int CHARACTER_MAX_GEM_PRESETS = 3;
|
||||
public static final int CHARACTER_MAX_GEM_SLOTS = 3;
|
||||
|
||||
public static final int MAX_FORMATIONS = 5;
|
||||
public static final int MAX_FORMATIONS = 6;
|
||||
public static final int MAX_SHOWCASE_IDS = 5;
|
||||
|
||||
public static final int BATTLE_PASS_ID = 1;
|
||||
@@ -37,6 +51,20 @@ 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},
|
||||
new int[] {300, 200}
|
||||
};
|
||||
|
||||
// Daily gifts (Custom)
|
||||
|
||||
public static final WeightedList<ItemParam> DAILY_GIFTS = new WeightedList<>();
|
||||
@@ -44,13 +72,21 @@ public class GameConstants {
|
||||
static {
|
||||
DAILY_GIFTS.add(1000, new ItemParam(GOLD_ITEM_ID, 8888));
|
||||
DAILY_GIFTS.add(250, new ItemParam(GOLD_ITEM_ID, 18888));
|
||||
DAILY_GIFTS.add(250, new ItemParam(33001, 10));
|
||||
DAILY_GIFTS.add(10, new ItemParam(GEM_ITEM_ID, 50));
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
public static String getGameVersion() {
|
||||
return VERSION + "." + getDataVersion();
|
||||
// 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() {
|
||||
|
||||
@@ -17,6 +17,7 @@ import emu.nebula.game.GameContext;
|
||||
import emu.nebula.net.PacketHelper;
|
||||
import emu.nebula.plugin.PluginManager;
|
||||
import emu.nebula.server.HttpServer;
|
||||
import emu.nebula.util.AeadHelper;
|
||||
import emu.nebula.util.Handbook;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import lombok.Getter;
|
||||
@@ -52,6 +53,9 @@ public class Nebula {
|
||||
|
||||
boolean generateHandbook = true;
|
||||
|
||||
// Load keys
|
||||
AeadHelper.loadKeys();
|
||||
|
||||
// Load plugin manager
|
||||
Nebula.pluginManager = new PluginManager();
|
||||
|
||||
@@ -107,7 +111,7 @@ public class Nebula {
|
||||
|
||||
// Start servers
|
||||
try {
|
||||
// Always run http server as it is needed by for dispatch and gateserver
|
||||
// Always run http server as it is needed by for login and game server
|
||||
httpServer = new HttpServer(serverType);
|
||||
httpServer.start();
|
||||
} catch (Exception exception) {
|
||||
@@ -233,7 +237,8 @@ public class Nebula {
|
||||
String input;
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||
while ((input = br.readLine()) != null) {
|
||||
Nebula.getCommandManager().invoke(null, input);
|
||||
var result = Nebula.getCommandManager().invoke(null, input);
|
||||
Nebula.getLogger().info(result.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Console error:", e);
|
||||
|
||||
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;
|
||||
@@ -26,6 +26,7 @@ public class CommandArgs {
|
||||
private int advance = -1;
|
||||
private int talent = -1;
|
||||
private int skill = -1;
|
||||
private int affinity = -1;
|
||||
|
||||
private Int2IntMap map;
|
||||
private ObjectSet<String> flags;
|
||||
@@ -52,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();
|
||||
@@ -61,6 +65,9 @@ public class CommandArgs {
|
||||
} else if (arg.startsWith("s")) { // Skill
|
||||
this.skill = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("f")) { // Affinity level
|
||||
this.affinity = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
}
|
||||
} else if (arg.startsWith("-")) { // Flag
|
||||
if (this.flags == null) this.flags = new ObjectOpenHashSet<>();
|
||||
@@ -72,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();
|
||||
@@ -109,18 +116,6 @@ public class CommandArgs {
|
||||
return this.list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the command sender
|
||||
* @param message
|
||||
*/
|
||||
public void sendMessage(String message) {
|
||||
if (sender != null) {
|
||||
sender.sendMessage(message);
|
||||
} else {
|
||||
Nebula.getLogger().info(message);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasFlag(String flag) {
|
||||
if (this.flags == null) return false;
|
||||
return this.flags.contains(flag);
|
||||
@@ -190,6 +185,16 @@ public class CommandArgs {
|
||||
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
if (this.getAffinity() >= 0) {
|
||||
int target = this.getAffinity();
|
||||
if (target > 50) target = 50;
|
||||
if (target < 0) target = 0;
|
||||
if (character.getAffinityLevel() != target) {
|
||||
character.setAffinityLevel(target);
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ public interface CommandHandler {
|
||||
return getData().label();
|
||||
}
|
||||
|
||||
public void execute(CommandArgs args);
|
||||
public String execute(CommandArgs args);
|
||||
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class CommandManager {
|
||||
return sender.getAccount().hasPermission("target." + command.permission());
|
||||
}
|
||||
|
||||
public void invoke(Player sender, String message) {
|
||||
public CommandResult invoke(Player sender, String message) {
|
||||
// Parse message into arguments
|
||||
List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new));
|
||||
|
||||
@@ -122,6 +122,9 @@ public class CommandManager {
|
||||
|
||||
// Get command handler
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
|
||||
// Create result object
|
||||
var result = CommandResult.builder();
|
||||
|
||||
// Execute command
|
||||
if (handler != null) {
|
||||
@@ -131,8 +134,8 @@ public class CommandManager {
|
||||
// Check if sender has permission to run the command.
|
||||
if (sender != null && !this.checkPermission(sender, command)) {
|
||||
// We have a double null check here just in case
|
||||
sender.sendMessage("You do not have permission to use this command.");
|
||||
return;
|
||||
result.message("Error - You do not have permission to use this command");
|
||||
return result.build();
|
||||
}
|
||||
|
||||
// Build command arguments
|
||||
@@ -140,14 +143,14 @@ public class CommandManager {
|
||||
|
||||
// Check targeted permission
|
||||
if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) {
|
||||
cmdArgs.sendMessage("You do not have permission to use this command on another player.");
|
||||
return;
|
||||
result.message("Error - You do not have permission to use this command on another player");
|
||||
return result.build();
|
||||
}
|
||||
|
||||
// Make sure our command has a target
|
||||
if (command.requireTarget() && cmdArgs.getTarget() == null) {
|
||||
cmdArgs.sendMessage("Error: Targeted player not found or offline");
|
||||
return;
|
||||
result.message("Error - Targeted player not found or offline");
|
||||
return result.build();
|
||||
}
|
||||
|
||||
// Log
|
||||
@@ -156,13 +159,20 @@ public class CommandManager {
|
||||
}
|
||||
|
||||
// Run command
|
||||
handler.execute(cmdArgs);
|
||||
} else {
|
||||
if (sender != null) {
|
||||
sender.sendMessage("Invalid Command!");
|
||||
} else {
|
||||
Nebula.getLogger().info("Invalid Command!");
|
||||
String commandMessage = handler.execute(cmdArgs);
|
||||
|
||||
// Parse out last newline
|
||||
if (commandMessage.endsWith("\n")) {
|
||||
commandMessage = commandMessage.substring(0, commandMessage.length() - 1);
|
||||
}
|
||||
|
||||
// Set result data
|
||||
result.command(handler);
|
||||
result.message(commandMessage);
|
||||
} else {
|
||||
result.message("Invalid Command!");
|
||||
}
|
||||
|
||||
return result.build();
|
||||
}
|
||||
}
|
||||
|
||||
19
src/main/java/emu/nebula/command/CommandResult.java
Normal file
19
src/main/java/emu/nebula/command/CommandResult.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package emu.nebula.command;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class CommandResult {
|
||||
private CommandHandler command;
|
||||
private String message;
|
||||
|
||||
public boolean isCommandTypeOf(Class<? extends CommandHandler> handler) {
|
||||
if (command == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return command.getClass().equals(handler);
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,9 @@ import emu.nebula.util.Utils;
|
||||
public class AccountCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
if (args.size() < 2) {
|
||||
args.sendMessage("Invalid amount of args");
|
||||
return;
|
||||
return "Invalid amount of args";
|
||||
}
|
||||
|
||||
String command = args.get(0).toLowerCase();
|
||||
@@ -29,19 +28,22 @@ public class AccountCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
if (AccountHelper.createAccount(username, null, reservedUid) != null) {
|
||||
args.sendMessage("Account created");
|
||||
return "Account created";
|
||||
} else {
|
||||
args.sendMessage("Account already exists");
|
||||
return "Account already exists";
|
||||
}
|
||||
}
|
||||
case "delete" -> {
|
||||
if (AccountHelper.deleteAccount(username)) {
|
||||
args.sendMessage("Account deleted");
|
||||
return "Account deleted";
|
||||
} else {
|
||||
args.sendMessage("Account doesnt exist");
|
||||
return "Account doesnt exist";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback
|
||||
return "Account 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). mMdifies your 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.";
|
||||
}
|
||||
|
||||
}
|
||||
179
src/main/java/emu/nebula/command/commands/BuildCommand.java
Normal file
179
src/main/java/emu/nebula/command/commands/BuildCommand.java
Normal file
@@ -0,0 +1,179 @@
|
||||
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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,13 @@ import emu.nebula.command.CommandHandler;
|
||||
label = "character",
|
||||
aliases = {"c", "char"},
|
||||
permission = "player.character",
|
||||
requireTarget = true,
|
||||
desc = "!c [all | {characterId}] lv(level) a(ascension) s(skill level) t(talent level)"
|
||||
requireTarget = true,
|
||||
desc = "!c [all | {characterId}] lv(level) a(ascension) s(skill level) t(talent level) f(affinity level)"
|
||||
)
|
||||
public class CharacterCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
// Init
|
||||
var player = args.getTarget();
|
||||
var characters = new HashSet<GameCharacter>();
|
||||
@@ -51,7 +51,7 @@ public class CharacterCommand implements CommandHandler {
|
||||
|
||||
// Sanity check
|
||||
if (characters.isEmpty()) {
|
||||
return;
|
||||
return "Error: No characters selected";
|
||||
}
|
||||
|
||||
// List of modified characters that we send to the client for updates
|
||||
@@ -72,7 +72,7 @@ public class CharacterCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
if (modified.isEmpty()) {
|
||||
return;
|
||||
return "No changes applied";
|
||||
}
|
||||
|
||||
// Encode and send
|
||||
@@ -83,5 +83,6 @@ public class CharacterCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
player.addNextPackage(NetMsgId.chars_final_notify, proto);
|
||||
return "Updated " + modified.size() + " character(s)";
|
||||
}
|
||||
}
|
||||
|
||||
129
src/main/java/emu/nebula/command/commands/CleanCommand.java
Normal file
129
src/main/java/emu/nebula/command/commands/CleanCommand.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.ItemDef;
|
||||
import emu.nebula.game.inventory.GameItem;
|
||||
import emu.nebula.game.inventory.GameResource;
|
||||
import emu.nebula.game.inventory.ItemParamMap;
|
||||
import emu.nebula.game.inventory.ItemType;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
@Command(
|
||||
label = "clean",
|
||||
aliases = {"cl", "clear"},
|
||||
permission = "player.inventory",
|
||||
requireTarget = true,
|
||||
desc = "!clean [all | {id} ...] [items|resources]. Removes items/resources from the targeted player."
|
||||
)
|
||||
public class CleanCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
var player = args.getTarget();
|
||||
var inv = player.getInventory();
|
||||
|
||||
boolean doItems = true;
|
||||
boolean doResources = true;
|
||||
boolean all = false;
|
||||
|
||||
var ids = new HashSet<Integer>();
|
||||
|
||||
for (String arg : args.getList()) {
|
||||
arg = arg.toLowerCase();
|
||||
|
||||
switch (arg) {
|
||||
case "items" -> {
|
||||
doResources = false;
|
||||
continue;
|
||||
}
|
||||
case "resources", "res" -> {
|
||||
doItems = false;
|
||||
continue;
|
||||
}
|
||||
case "all" -> {
|
||||
all = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int id = Utils.parseSafeInt(arg);
|
||||
if (id > 0) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
var change = new PlayerChangeInfo();
|
||||
var removeMap = new ItemParamMap();
|
||||
|
||||
if (all) {
|
||||
if (doItems) {
|
||||
for (GameItem item : inv.getItems().values()) {
|
||||
removeMap.add(item.getItemId(), item.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
if (doResources) {
|
||||
for (GameResource res : inv.getResources().values()) {
|
||||
removeMap.add(res.getResourceId(), res.getCount());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int id : ids) {
|
||||
ItemDef data = GameData.getItemDataTable().get(id);
|
||||
if (data == null) continue;
|
||||
|
||||
ItemType type = data.getItemType();
|
||||
|
||||
switch (type) {
|
||||
case Res -> {
|
||||
if (doResources) {
|
||||
int count = inv.getResourceCount(id);
|
||||
if (count > 0) removeMap.add(id, count);
|
||||
}
|
||||
}
|
||||
case Item -> {
|
||||
if (doItems) {
|
||||
int count = inv.getItemCount(id);
|
||||
if (count > 0) removeMap.add(id, count);
|
||||
}
|
||||
}
|
||||
case Disc, Char, CharacterSkin, Title, Honor -> {
|
||||
|
||||
}
|
||||
default -> {
|
||||
if (doItems) {
|
||||
int count = inv.getItemCount(id);
|
||||
if (count > 0) {
|
||||
removeMap.add(id, count);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (doResources) {
|
||||
int count = inv.getResourceCount(id);
|
||||
if (count > 0) removeMap.add(id, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!removeMap.isEmpty()) {
|
||||
change = inv.removeItems(removeMap, change);
|
||||
}
|
||||
|
||||
if (change.isEmpty()) {
|
||||
return "No items/resources removed";
|
||||
}
|
||||
|
||||
player.addNextPackage(NetMsgId.items_change_notify, change.toProto());
|
||||
|
||||
return "Inventory cleaned";
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import emu.nebula.command.CommandHandler;
|
||||
public class DiscCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
// Init
|
||||
var player = args.getTarget();
|
||||
var discs = new HashSet<GameDisc>();
|
||||
@@ -50,7 +50,7 @@ public class DiscCommand implements CommandHandler {
|
||||
|
||||
// Sanity check
|
||||
if (discs.isEmpty()) {
|
||||
return;
|
||||
return "No discs selected";
|
||||
}
|
||||
|
||||
// List of modified characters that we send to the client for updates
|
||||
@@ -71,12 +71,15 @@ public class DiscCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
if (modified.isEmpty()) {
|
||||
return;
|
||||
return "No discs changed";
|
||||
}
|
||||
|
||||
// Encode and send
|
||||
for (var disc : modified) {
|
||||
player.addNextPackage(NetMsgId.disc_reset_notify, disc.toProto());
|
||||
}
|
||||
|
||||
// Return message
|
||||
return "Changed " + modified.size() + " discs";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,14 @@ public class GiveAllCommand implements CommandHandler {
|
||||
);
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
Player target = args.getTarget();
|
||||
String type = args.get(0).toLowerCase();
|
||||
|
||||
|
||||
var change = new PlayerChangeInfo();
|
||||
var message = new StringBuilder();
|
||||
|
||||
switch (type) {
|
||||
default -> args.sendMessage("Error: Invalid type");
|
||||
case "m", "materials", "mats" -> {
|
||||
// Create items map
|
||||
var items = new ItemParamMap();
|
||||
@@ -66,7 +65,7 @@ public class GiveAllCommand implements CommandHandler {
|
||||
target.getInventory().addItems(items, change);
|
||||
|
||||
// Send message
|
||||
args.sendMessage("Giving " + target.getName() + " " + items.size() + " items");
|
||||
message.append("Giving " + target.getName() + " " + items.size() + " items.\n");
|
||||
}
|
||||
case "d", "discs" -> {
|
||||
// Get all discs
|
||||
@@ -92,7 +91,7 @@ public class GiveAllCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
// Send message
|
||||
args.sendMessage("Giving " + target.getName() + " all discs");
|
||||
message.append("Giving " + target.getName() + " all discs.\n");
|
||||
}
|
||||
case "c", "characters", "trekkers", "t" -> {
|
||||
// Get all characters
|
||||
@@ -118,16 +117,22 @@ public class GiveAllCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
// Send message
|
||||
args.sendMessage("Giving " + target.getName() + " all characters");
|
||||
message.append("Giving " + target.getName() + " all characters.\n");
|
||||
}
|
||||
default -> {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
if (change.isEmpty()) {
|
||||
return;
|
||||
return "No items given to the player";
|
||||
}
|
||||
|
||||
// Encode and send
|
||||
target.addNextPackage(NetMsgId.items_change_notify, change.toProto());
|
||||
|
||||
// Complete
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import emu.nebula.command.CommandHandler;
|
||||
public class GiveCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
// Setup mail
|
||||
var mail = new GameMail("System", "Give Command Result", "");
|
||||
|
||||
@@ -31,18 +31,17 @@ public class GiveCommand implements CommandHandler {
|
||||
|
||||
var itemData = GameData.getItemDataTable().get(itemId);
|
||||
if (itemData == null) {
|
||||
args.sendMessage("Item \"" + arg + "\" does not exist!");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add
|
||||
mail.addAttachment(itemId, amount);
|
||||
|
||||
// Log
|
||||
args.sendMessage("Giving " + args.getTarget().getName() + " " + amount + " of " + itemId);
|
||||
}
|
||||
|
||||
// Add mail
|
||||
args.getTarget().getMailbox().sendMail(mail);
|
||||
|
||||
//
|
||||
return "Give command success, check your mail";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import emu.nebula.command.CommandHandler;
|
||||
public class HelpCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
args.sendMessage("Displaying list of commands:");
|
||||
public String execute(CommandArgs args) {
|
||||
var message = new StringBuilder();
|
||||
|
||||
message.append("Displaying list of commands:\n");
|
||||
|
||||
// Sort command names
|
||||
var labels = Nebula.getCommandManager().getLabels().keySet().stream().sorted().toList();
|
||||
@@ -21,9 +23,11 @@ public class HelpCommand implements CommandHandler {
|
||||
|
||||
// Only send command description if the sender has permission to use the command
|
||||
if (Nebula.getCommandManager().checkPermission(args.getSender(), command)) {
|
||||
args.sendMessage(command.desc());
|
||||
message.append(command.desc());
|
||||
message.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,41 +17,36 @@ import emu.nebula.util.Utils;
|
||||
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 ...]";
|
||||
private static final Pattern QUOTED_TEXT = Pattern.compile("\"([^\"]*)\"");
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
var target = args.getTarget();
|
||||
if (target == null) {
|
||||
args.sendMessage("Error: Targeted player not found or offline");
|
||||
return;
|
||||
return "Error - Targeted player not found or offline";
|
||||
}
|
||||
|
||||
String rawInput = args.getRaw() == null ? "" : args.getRaw().trim();
|
||||
if (rawInput.isEmpty()) {
|
||||
sendUsage(args);
|
||||
return;
|
||||
return USAGE_TEXT;
|
||||
}
|
||||
|
||||
Matcher matcher = QUOTED_TEXT.matcher(rawInput);
|
||||
if (!matcher.find()) {
|
||||
sendUsage(args);
|
||||
return;
|
||||
return USAGE_TEXT;
|
||||
}
|
||||
|
||||
String subject = matcher.group(1).trim();
|
||||
if (!matcher.find()) {
|
||||
args.sendMessage("Mail body must be wrapped in quotes after the subject.");
|
||||
return;
|
||||
return "Mail body must be wrapped in quotes after the subject.";
|
||||
}
|
||||
|
||||
String body = matcher.group(1).trim();
|
||||
int attachmentStartIndex = matcher.end();
|
||||
|
||||
if (subject.isEmpty()) {
|
||||
args.sendMessage("Mail subject cannot be empty.");
|
||||
return;
|
||||
return "Mail subject cannot be empty.";
|
||||
}
|
||||
|
||||
if (body.isEmpty()) {
|
||||
@@ -68,7 +63,7 @@ public class MailCommand implements CommandHandler {
|
||||
parseAttachments(attachmentSection, mail, args);
|
||||
|
||||
target.getMailbox().sendMail(mail);
|
||||
args.sendMessage("Mail sent to " + target.getName() + " with subject \"" + subject + "\".");
|
||||
return "Mail sent to " + target.getName() + " with subject \"" + subject + "\".";
|
||||
}
|
||||
|
||||
private void parseAttachments(String attachmentSection, GameMail mail, CommandArgs args) {
|
||||
@@ -93,7 +88,7 @@ public class MailCommand implements CommandHandler {
|
||||
|
||||
if (token.startsWith("x") && token.length() > 1) {
|
||||
if (pendingItemId == null) {
|
||||
args.sendMessage("Quantity token '" + token + "' must follow an item id.");
|
||||
//args.sendMessage("Quantity token '" + token + "' must follow an item id.");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -110,7 +105,7 @@ public class MailCommand implements CommandHandler {
|
||||
|
||||
int itemId = Utils.parseSafeInt(token);
|
||||
if (itemId <= 0) {
|
||||
args.sendMessage("Invalid item id '" + token + "'.");
|
||||
//args.sendMessage("Invalid item id '" + token + "'.");
|
||||
pendingItemId = null;
|
||||
continue;
|
||||
}
|
||||
@@ -119,7 +114,7 @@ public class MailCommand implements CommandHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
args.sendMessage("Ignoring attachment token '" + token + "'.");
|
||||
//args.sendMessage("Ignoring attachment token '" + token + "'.");
|
||||
}
|
||||
|
||||
if (pendingItemId != null) {
|
||||
@@ -129,7 +124,7 @@ public class MailCommand implements CommandHandler {
|
||||
|
||||
private void addAttachment(GameMail mail, CommandArgs args, int itemId, int quantity) {
|
||||
if (itemId <= 0) {
|
||||
args.sendMessage("Item id must be positive.");
|
||||
//args.sendMessage("Item id must be positive.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -145,8 +140,4 @@ public class MailCommand implements CommandHandler {
|
||||
}
|
||||
return !token.isEmpty();
|
||||
}
|
||||
|
||||
private void sendUsage(CommandArgs args) {
|
||||
args.sendMessage("Usage: /mail \"subject\" \"body\" [itemId xQty | itemId:qty ...]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@ import emu.nebula.command.CommandHandler;
|
||||
public class ReloadCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(CommandArgs args) {
|
||||
public String execute(CommandArgs args) {
|
||||
// Reload config first
|
||||
Nebula.loadConfig();
|
||||
|
||||
// Reload patch list if the server is running
|
||||
if (Nebula.getHttpServer() != null) {
|
||||
Nebula.getHttpServer().loadPatchList();
|
||||
}
|
||||
|
||||
args.sendMessage("Reloaded the server config");
|
||||
// Result message
|
||||
return "Reloaded the server config";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
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 java.util.Random;
|
||||
|
||||
@Command(label = "remote", permission = "player.remote", requireTarget = true, desc = "/remote. Send remote to web remote")
|
||||
public class RemoteKeyCommand implements CommandHandler {
|
||||
private final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
if (Nebula.getConfig().getRemoteCommand().useRemoteServices) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Random random = new Random();
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int index = random.nextInt(characters.length());
|
||||
sb.append(characters.charAt(index));
|
||||
}
|
||||
|
||||
args.getTarget().setRemoteToken(sb.toString());
|
||||
return "Key Generated: " + sb.toString();
|
||||
} else {
|
||||
args.getTarget().setRemoteToken(null);
|
||||
return "Remote Command Disabled on Server";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package emu.nebula.command.commands;
|
||||
|
||||
import emu.nebula.command.Command;
|
||||
import emu.nebula.command.CommandArgs;
|
||||
import emu.nebula.command.CommandHandler;
|
||||
import emu.nebula.data.GameData;
|
||||
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 {
|
||||
|
||||
@Override
|
||||
public String execute(CommandArgs args) {
|
||||
// Get target
|
||||
var target = args.getTarget();
|
||||
|
||||
// Parse level
|
||||
int level = Utils.parseSafeInt(args.get(0));
|
||||
|
||||
// Check to make sure world class data exists for this level
|
||||
var data = GameData.getWorldClassDataTable().get(level);
|
||||
if (data == null) {
|
||||
return "The game does not support level " + level;
|
||||
}
|
||||
|
||||
target.setLevel(level);
|
||||
target.setExp(0);
|
||||
|
||||
// Update
|
||||
target.addNextPackage(
|
||||
NetMsgId.world_class_number_notify,
|
||||
GmWorldClass.newInstance()
|
||||
.setFinalClass(target.getLevel())
|
||||
.setLastExp(target.getExp())
|
||||
);
|
||||
|
||||
return "Level set to " + level;
|
||||
}
|
||||
}
|
||||
@@ -28,14 +28,23 @@ public class GameData {
|
||||
@Getter private static DataTable<TalentGroupDef> TalentGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<TalentDef> TalentDataTable = new DataTable<>();
|
||||
|
||||
// Character 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<CharGemAttrTypeDef> CharGemAttrTypeDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharGemAttrValueDef> CharGemAttrValueDataTable = new DataTable<>();
|
||||
|
||||
// Character 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<>();
|
||||
|
||||
@Getter private static DataTable<ChatDef> ChatDataTable = new DataTable<>();
|
||||
|
||||
@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
|
||||
@Getter private static DataTable<DiscDef> DiscDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscStrengthenDef> DiscStrengthenDataTable = new DataTable<>();
|
||||
@@ -43,6 +52,8 @@ public class GameData {
|
||||
@Getter private static DataTable<DiscPromoteDef> DiscPromoteDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<DiscPromoteLimitDef> DiscPromoteLimitDataTable = new DataTable<>();
|
||||
|
||||
@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<>();
|
||||
@@ -93,6 +104,9 @@ public class GameData {
|
||||
@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<>();
|
||||
|
||||
@@ -100,9 +114,14 @@ public class GameData {
|
||||
@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<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerFloorExpDef> StarTowerFloorExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerTeamExpDef> StarTowerTeamExpDataTable = 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<>();
|
||||
@@ -117,4 +136,10 @@ public class GameData {
|
||||
|
||||
// Score boss
|
||||
@Getter private static DataTable<ScoreBossControlDef> ScoreBossControlDataTable = new DataTable<>();
|
||||
}
|
||||
|
||||
// Activity
|
||||
@Getter private static DataTable<ActivityDef> ActivityDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<TrialControlDef> TrialControlDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<TrialGroupDef> TrialGroupDataTable = new DataTable<>();
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package emu.nebula.data;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -9,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;
|
||||
@@ -21,6 +23,9 @@ public class ResourceLoader {
|
||||
// Load
|
||||
loadResources();
|
||||
|
||||
// Add hardcoded achievements params
|
||||
AchievementHelper.init();
|
||||
|
||||
// Done
|
||||
loaded = true;
|
||||
Nebula.getLogger().info("Resource loading complete");
|
||||
@@ -54,9 +59,26 @@ public class ResourceLoader {
|
||||
int count = 0;
|
||||
|
||||
try {
|
||||
var json = JsonUtils.loadToMap(Nebula.getConfig().resourceDir + "/bin/" + type.name(), String.class, resourceClass);
|
||||
// Init defs collection
|
||||
Iterable<?> defs = null;
|
||||
|
||||
// Load resource file
|
||||
if (type.useInternal()) {
|
||||
// Load from internal resources in jar
|
||||
try (var in = ResourceLoader.class.getResourceAsStream("/defs/" + type.name()); var reader = new InputStreamReader(in)) {
|
||||
defs = JsonUtils.loadToList(reader, resourceClass);
|
||||
} catch (Exception e) {
|
||||
// Ignored
|
||||
}
|
||||
} else {
|
||||
// Load json from ./resources/bin/ folder
|
||||
var json = JsonUtils.loadToMap(Nebula.getConfig().resourceDir + "/bin/" + type.name(), String.class, resourceClass);
|
||||
|
||||
// Get json values
|
||||
defs = json.values();
|
||||
}
|
||||
|
||||
for (Object o : json.values()) {
|
||||
for (Object o : defs) {
|
||||
BaseDef res = (BaseDef) o;
|
||||
|
||||
if (res == null) {
|
||||
|
||||
@@ -10,13 +10,15 @@ public @interface ResourceType {
|
||||
String name();
|
||||
|
||||
/**
|
||||
* Load priority - dictates which order to load this resource, with "highest"
|
||||
* being loaded first
|
||||
* Load priority - dictates which order to load this resource, with "highest" being loaded first
|
||||
*/
|
||||
LoadPriority loadPriority() default LoadPriority.NORMAL;
|
||||
|
||||
Class<?> keyType() default int.class;
|
||||
|
||||
/**
|
||||
* Loads the resource file from inside the jar (./resources/defs/) if set to true
|
||||
*/
|
||||
boolean useInternal() default false;
|
||||
|
||||
public enum LoadPriority {
|
||||
HIGHEST(4), HIGH(3), NORMAL(2), LOW(1), LOWEST(0);
|
||||
|
||||
|
||||
68
src/main/java/emu/nebula/data/resources/AchievementDef.java
Normal file
68
src/main/java/emu/nebula/data/resources/AchievementDef.java
Normal file
@@ -0,0 +1,68 @@
|
||||
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
|
||||
@ResourceType(name = "Achievement.json")
|
||||
public class AchievementDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Type;
|
||||
private int CompleteCond;
|
||||
private int AimNumShow;
|
||||
private int[] Prerequisites;
|
||||
|
||||
// Reward
|
||||
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);
|
||||
}
|
||||
}
|
||||
20
src/main/java/emu/nebula/data/resources/AffinityGiftDef.java
Normal file
20
src/main/java/emu/nebula/data/resources/AffinityGiftDef.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 = "AffinityGift.json")
|
||||
public class AffinityGiftDef extends BaseDef {
|
||||
private int Id;
|
||||
private int BaseAffinity;
|
||||
private int[] Tags;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 = "AffinityLevel.json")
|
||||
public class AffinityLevelDef extends BaseDef {
|
||||
private int AffinityLevel;
|
||||
private int NeedExp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return AffinityLevel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,15 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.inventory.ItemRewardList;
|
||||
import emu.nebula.game.inventory.ItemRewardParam;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
@@ -35,6 +40,8 @@ public class AgentDef extends BaseDef {
|
||||
private String BonusPreview4;
|
||||
|
||||
private transient Int2ObjectMap<AgentDurationDef> durations;
|
||||
private transient Int2IntOpenHashMap tags;
|
||||
private transient Int2IntOpenHashMap extraTags;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
@@ -45,14 +52,83 @@ public class AgentDef extends BaseDef {
|
||||
this.durations.put(duration.getTime(), duration);
|
||||
}
|
||||
|
||||
public boolean hasTags(List<GameCharacter> characters) {
|
||||
// Get character tags
|
||||
var characterTags = new Int2IntOpenHashMap();
|
||||
|
||||
for (var character : characters) {
|
||||
var data = character.getData().getDes();
|
||||
|
||||
for (int tag : data.getTag()) {
|
||||
characterTags.addTo(tag, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that we have the tags
|
||||
for (var entry : this.tags.int2IntEntrySet()) {
|
||||
int reqTagId = entry.getIntKey();
|
||||
int reqTagCount = entry.getIntValue();
|
||||
|
||||
// Get amount of tags that we have from our characters
|
||||
int characterTagCount = characterTags.get(reqTagId);
|
||||
|
||||
if (reqTagCount > characterTagCount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasExtraTags(List<GameCharacter> characters) {
|
||||
// Get character tags
|
||||
var characterTags = new Int2IntOpenHashMap();
|
||||
|
||||
for (var character : characters) {
|
||||
var data = character.getData().getDes();
|
||||
|
||||
for (int tag : data.getTag()) {
|
||||
characterTags.addTo(tag, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that we have the tags
|
||||
for (var entry : this.extraTags.int2IntEntrySet()) {
|
||||
int reqTagId = entry.getIntKey();
|
||||
int reqTagCount = entry.getIntValue();
|
||||
|
||||
// Get amount of tags that we have from our characters
|
||||
int characterTagCount = characterTags.get(reqTagId);
|
||||
|
||||
if (reqTagCount > characterTagCount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Cache durations
|
||||
this.durations = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.addDuration(new AgentDurationDef(this.Time1, this.RewardPreview1, this.BonusPreview1));
|
||||
this.addDuration(new AgentDurationDef(this.Time2, this.RewardPreview2, this.BonusPreview2));
|
||||
this.addDuration(new AgentDurationDef(this.Time3, this.RewardPreview3, this.BonusPreview3));
|
||||
this.addDuration(new AgentDurationDef(this.Time4, this.RewardPreview4, this.BonusPreview4));
|
||||
|
||||
// Cache tags
|
||||
this.tags = new Int2IntOpenHashMap();
|
||||
this.extraTags = new Int2IntOpenHashMap();
|
||||
|
||||
for (int tag : this.Tags) {
|
||||
this.tags.addTo(tag, 1);
|
||||
}
|
||||
|
||||
for (int tag : this.ExtraTags) {
|
||||
this.extraTags.addTo(tag, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -1,32 +1,36 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import emu.nebula.util.JsonUtils;
|
||||
import emu.nebula.util.WeightedList;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharGemAttrGroup.json", loadPriority = LoadPriority.HIGH)
|
||||
@ResourceType(name = "CharGemAttrGroups.json", useInternal = true)
|
||||
public class CharGemAttrGroupDef extends BaseDef {
|
||||
private int GroupId;
|
||||
private int GroupType;
|
||||
private int Id;
|
||||
private int Weight;
|
||||
private String UniqueAttrNumWeight;
|
||||
private IntArrayList AttrTypes;
|
||||
private Map<Integer, Integer> UniqueAttrNumWeights;
|
||||
|
||||
private transient WeightedList<Integer> uniqueAttrNum;
|
||||
private transient List<CharGemAttrTypeDef> attributeTypes;
|
||||
private transient List<CharGemAttrTypeData> attributeTypes;
|
||||
|
||||
public CharGemAttrGroupDef() {
|
||||
this.AttrTypes = new IntArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return GroupId;
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getRandomUniqueAttrNum() {
|
||||
@@ -37,7 +41,7 @@ public class CharGemAttrGroupDef extends BaseDef {
|
||||
return this.uniqueAttrNum.next();
|
||||
}
|
||||
|
||||
public CharGemAttrTypeDef getRandomAttributeType(IntList list) {
|
||||
public CharGemAttrTypeData getRandomAttributeType(IntList list) {
|
||||
// Setup blacklist to prevent the same attribute from showing up twice
|
||||
var blacklist = new IntOpenHashSet();
|
||||
|
||||
@@ -50,7 +54,7 @@ public class CharGemAttrGroupDef extends BaseDef {
|
||||
}
|
||||
|
||||
// Create random generator
|
||||
var random = new WeightedList<CharGemAttrTypeDef>();
|
||||
var random = new WeightedList<CharGemAttrTypeData>();
|
||||
|
||||
for (var type : this.getAttributeTypes()) {
|
||||
if (blacklist.contains(type.getId())) {
|
||||
@@ -65,15 +69,44 @@ public class CharGemAttrGroupDef extends BaseDef {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Init unique attribute weights
|
||||
this.uniqueAttrNum = new WeightedList<>();
|
||||
this.attributeTypes = new ObjectArrayList<>();
|
||||
|
||||
if (this.UniqueAttrNumWeight != null) {
|
||||
var json = JsonUtils.decodeMap(this.UniqueAttrNumWeight, Integer.class, Integer.class);
|
||||
|
||||
for (var entry : json.entrySet()) {
|
||||
if (this.UniqueAttrNumWeights != null) {
|
||||
for (var entry : this.UniqueAttrNumWeights.entrySet()) {
|
||||
this.uniqueAttrNum.add(entry.getValue(), entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
// Init attribute types
|
||||
this.attributeTypes = new ObjectArrayList<>();
|
||||
|
||||
for (int id : this.getAttrTypes()) {
|
||||
var type = new CharGemAttrTypeData(id);
|
||||
this.attributeTypes.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
public static class CharGemAttrTypeData {
|
||||
private int id;
|
||||
private WeightedList<CharGemAttrValueDef> values;
|
||||
|
||||
public CharGemAttrTypeData(int id) {
|
||||
this.id = id;
|
||||
this.values = new WeightedList<>();
|
||||
}
|
||||
|
||||
protected void addValue(CharGemAttrValueDef value) {
|
||||
this.values.add(value.getRarity(), value);
|
||||
}
|
||||
|
||||
public CharGemAttrValueDef getRandomValueData() {
|
||||
return this.getValues().next();
|
||||
}
|
||||
|
||||
public int getRandomValue() {
|
||||
return this.getRandomValueData().getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.util.WeightedList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharGemAttrType.json")
|
||||
public class CharGemAttrTypeDef extends BaseDef {
|
||||
private int Id;
|
||||
private int GroupId;
|
||||
|
||||
private transient WeightedList<CharGemAttrValueDef> values;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public CharGemAttrValueDef getRandomValueData() {
|
||||
return this.getValues().next();
|
||||
}
|
||||
|
||||
public int getRandomValue() {
|
||||
return this.getRandomValueData().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.values = new WeightedList<>();
|
||||
|
||||
var data = GameData.getCharGemAttrGroupDataTable().get(this.GroupId);
|
||||
if (data != null) {
|
||||
data.getAttributeTypes().add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ public class CharGemAttrValueDef extends BaseDef {
|
||||
private int Id;
|
||||
private int TypeId;
|
||||
private int AttrType;
|
||||
private int AttrTypeFirstSubtype;
|
||||
private int AttrTypeSecondSubtype;
|
||||
private int Rarity;
|
||||
|
||||
@Override
|
||||
@@ -23,9 +21,18 @@ public class CharGemAttrValueDef extends BaseDef {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
var data = GameData.getCharGemAttrTypeDataTable().get(this.TypeId);
|
||||
if (data != null) {
|
||||
data.getValues().add(this.getRarity(), this);
|
||||
// Cache attribute values into attribute group defs
|
||||
// Honestly a horrible/inefficient way of doing this
|
||||
for (var data : GameData.getCharGemAttrGroupDataTable()) {
|
||||
for (var type : data.getAttributeTypes()) {
|
||||
// Skip if type id doesnt match
|
||||
if (type.getId() != this.getTypeId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add
|
||||
type.addValue(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.util.CustomIntArray;
|
||||
import emu.nebula.util.Utils;
|
||||
import emu.nebula.util.WeightedList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
@@ -19,16 +20,15 @@ public class CharGemSlotControlDef extends BaseDef {
|
||||
private int GeneratenCostQty;
|
||||
private int RefreshCostQty;
|
||||
|
||||
private int UniqueAttrGroupProb;
|
||||
private int UniqueAttrGroupId;
|
||||
private int GuaranteeCount;
|
||||
|
||||
private int[] AttrGroupId;
|
||||
|
||||
private int LockableNum;
|
||||
private int LockItemTid;
|
||||
private int LockItemQty;
|
||||
|
||||
// These have to be hardcoded now
|
||||
private transient int UniqueAttrGroupProb;
|
||||
private transient int UniqueAttrGroupId;
|
||||
private transient int[] AttrGroupId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
@@ -38,9 +38,15 @@ public class CharGemSlotControlDef extends BaseDef {
|
||||
return this.generateAttributes(new CustomIntArray());
|
||||
}
|
||||
|
||||
// Check if we should add unique attributes based on the probablity
|
||||
private boolean shouldAddUniqueAttr() {
|
||||
int random = Utils.randomRange(1, 10000);
|
||||
return random <= this.UniqueAttrGroupProb;
|
||||
}
|
||||
|
||||
public IntList generateAttributes(CustomIntArray list) {
|
||||
// Add unique attributes
|
||||
if (this.UniqueAttrGroupId > 0) {
|
||||
if (this.UniqueAttrGroupId > 0 && this.shouldAddUniqueAttr()) {
|
||||
var group = GameData.getCharGemAttrGroupDataTable().get(this.UniqueAttrGroupId);
|
||||
int num = group.getRandomUniqueAttrNum();
|
||||
|
||||
@@ -49,7 +55,7 @@ public class CharGemSlotControlDef extends BaseDef {
|
||||
list.add(attributeType.getRandomValue());
|
||||
}
|
||||
|
||||
if (list.size() >= this.MaxAlterNum) {
|
||||
if (list.getValueCount() >= this.MaxAlterNum) {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -76,4 +82,25 @@ public class CharGemSlotControlDef extends BaseDef {
|
||||
// Complete
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Hard coded values
|
||||
switch (this.Id) {
|
||||
case 3:
|
||||
this.UniqueAttrGroupProb = 3300;
|
||||
this.UniqueAttrGroupId = 12;
|
||||
this.AttrGroupId = new int[] {9, 10};
|
||||
break;
|
||||
case 2:
|
||||
this.UniqueAttrGroupProb = 4000;
|
||||
this.UniqueAttrGroupId = 11;
|
||||
this.AttrGroupId = new int[] {5, 6, 7, 8};
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
this.AttrGroupId = new int[] {1, 2, 3, 4};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ public class CharacterDef extends BaseDef {
|
||||
|
||||
private int[] GemSlots;
|
||||
|
||||
private transient CharacterDesDef des;
|
||||
private transient ElementType elementType;
|
||||
private transient List<ChatDef> chats;
|
||||
|
||||
@@ -38,6 +39,10 @@ public class CharacterDef extends BaseDef {
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
protected void setDes(CharacterDesDef des) {
|
||||
this.des = des;
|
||||
}
|
||||
|
||||
public int getSkillsUpgradeGroup(int index) {
|
||||
if (index < 0 || index >= this.SkillsUpgradeGroup.length) {
|
||||
|
||||
60
src/main/java/emu/nebula/data/resources/CharacterDesDef.java
Normal file
60
src/main/java/emu/nebula/data/resources/CharacterDesDef.java
Normal file
@@ -0,0 +1,60 @@
|
||||
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 it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "CharacterDes.json", loadPriority = LoadPriority.LOW)
|
||||
public class CharacterDesDef extends BaseDef {
|
||||
private int Id;
|
||||
private int[] Tag;
|
||||
private IntOpenHashSet PreferTags;
|
||||
private IntOpenHashSet HateTags;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public boolean isPreferGift(AffinityGiftDef gift) {
|
||||
if (this.getPreferTags() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < gift.getTags().length; i++) {
|
||||
int tag = gift.getTags()[i];
|
||||
if (this.getPreferTags().contains(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isHateGift(AffinityGiftDef gift) {
|
||||
if (this.getHateTags() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < gift.getTags().length; i++) {
|
||||
int tag = gift.getTags()[i];
|
||||
if (this.getHateTags().contains(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
var character = GameData.getCharacterDataTable().get(this.getId());
|
||||
if (character != null) {
|
||||
character.setDes(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/main/java/emu/nebula/data/resources/DatingBranchDef.java
Normal file
32
src/main/java/emu/nebula/data/resources/DatingBranchDef.java
Normal file
@@ -0,0 +1,32 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DatingBranch.json", loadPriority = LoadPriority.LOW)
|
||||
public class DatingBranchDef extends BaseDef {
|
||||
private int Id;
|
||||
private int DatingEventType;
|
||||
private int[] DatingEventParams;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getLandmarkId() {
|
||||
if (this.DatingEventParams.length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.DatingEventParams[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.ResourceType.LoadPriority;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DatingCharacterEvent.json", loadPriority = LoadPriority.LOW)
|
||||
public class DatingCharacterEventDef extends DatingLandmarkEventDef {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.data.BaseDef;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.game.dating.DatingEvent;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DatingLandmark.json")
|
||||
public class DatingLandmarkDef extends BaseDef {
|
||||
private int Id;
|
||||
|
||||
private transient List<DatingEvent> afterBranches;
|
||||
private transient Object2ObjectMap<String, List<DatingEvent>> characterEvents;
|
||||
private transient Object2ObjectMap<String, List<DatingEvent>> landmarkEvents;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getRandomAfterBranchId() {
|
||||
var event = Utils.randomElement(this.afterBranches);
|
||||
|
||||
if (event == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return event.getId();
|
||||
}
|
||||
|
||||
public int getRandomCharacterEventId() {
|
||||
var list = new ArrayList<DatingEvent>();
|
||||
|
||||
for (var events : this.characterEvents.values()) {
|
||||
list.addAll(events);
|
||||
}
|
||||
|
||||
// Get random event
|
||||
var event = Utils.randomElement(list);
|
||||
|
||||
if (event == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return event.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.afterBranches = new ArrayList<>();
|
||||
this.characterEvents = new Object2ObjectOpenHashMap<>();
|
||||
this.landmarkEvents = new Object2ObjectOpenHashMap<>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package emu.nebula.data.resources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
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.dating.DatingEvent;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "DatingLandmarkEvent.json", loadPriority = LoadPriority.LOW)
|
||||
public class DatingLandmarkEventDef extends BaseDef implements DatingEvent {
|
||||
private int Id;
|
||||
private int DatingEventType;
|
||||
private int Affinity;
|
||||
private int[] DatingEventParams;
|
||||
private String Response;
|
||||
|
||||
private transient emu.nebula.game.dating.DatingEventType type;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public int getLandmarkId() {
|
||||
if (this.DatingEventParams.length <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.DatingEventParams[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Cache dating event type
|
||||
this.type = emu.nebula.game.dating.DatingEventType.getByValue(this.getDatingEventType());
|
||||
|
||||
// Add to landmark data
|
||||
var data = GameData.getDatingLandmarkDataTable().get(this.getLandmarkId());
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.getType()) {
|
||||
case Landmark -> {
|
||||
data.getLandmarkEvents()
|
||||
.computeIfAbsent(this.getResponse(), s -> new ArrayList<>())
|
||||
.add(this);
|
||||
}
|
||||
case Regular -> {
|
||||
data.getCharacterEvents()
|
||||
.computeIfAbsent(this.getResponse(), s -> new ArrayList<>())
|
||||
.add(this);
|
||||
}
|
||||
case AfterBranch -> {
|
||||
data.getAfterBranches().add(this);
|
||||
}
|
||||
default -> {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
31
src/main/java/emu/nebula/data/resources/PlotDef.java
Normal file
31
src/main/java/emu/nebula/data/resources/PlotDef.java
Normal file
@@ -0,0 +1,31 @@
|
||||
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 = "Plot.json")
|
||||
public class PlotDef extends BaseDef {
|
||||
private int Id;
|
||||
private int Char;
|
||||
private int UnlockAffinityLevel;
|
||||
private String Rewards;
|
||||
|
||||
private transient ItemParamMap rewards;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.Id;
|
||||
}
|
||||
|
||||
public ItemParamMap getRewards() {
|
||||
return this.rewards;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.rewards = ItemParamMap.fromJsonString(this.Rewards);
|
||||
}
|
||||
}
|
||||
@@ -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,44 @@ 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 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().getBonusMaxPotentialLevel();
|
||||
}
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 = "StarTowerFloorExp.json")
|
||||
public class StarTowerFloorExpDef extends BaseDef {
|
||||
private int Id;
|
||||
private int StarTowerId;
|
||||
private int Stage;
|
||||
private int NormalExp;
|
||||
private int EliteExp;
|
||||
private int BossExp;
|
||||
private int FinalBossExp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ public class StarTowerStageDef extends BaseDef {
|
||||
private int Stage;
|
||||
private int Floor;
|
||||
private int RoomType;
|
||||
private int InteriorCurrencyQuantity;
|
||||
|
||||
@Override
|
||||
public int 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 = "StarTowerTeamExp.json")
|
||||
public class StarTowerTeamExpDef extends BaseDef {
|
||||
private int Id;
|
||||
private int GroupId;
|
||||
private int Level;
|
||||
private int NeedExp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,18 @@ public class VampireSurvivorDef extends BaseDef {
|
||||
private int NeedWorldClass;
|
||||
private int[] FateCardBundle;
|
||||
|
||||
private int NormalScore1;
|
||||
private int EliteScore1;
|
||||
private int BossScore1;
|
||||
private int TimeScore1;
|
||||
private int TimeLimit1;
|
||||
|
||||
private int NormalScore2;
|
||||
private int EliteScore2;
|
||||
private int BossScore2;
|
||||
private int TimeScore2;
|
||||
private int TimeLimit2;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return Id;
|
||||
|
||||
@@ -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,12 +8,13 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.game.activity.ActivityModule;
|
||||
import emu.nebula.game.gacha.GachaModule;
|
||||
import emu.nebula.game.player.PlayerModule;
|
||||
import emu.nebula.game.scoreboss.ScoreBossModule;
|
||||
import emu.nebula.game.tutorial.TutorialModule;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
||||
@@ -27,6 +28,7 @@ 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;
|
||||
|
||||
// Game loop
|
||||
@@ -34,6 +36,7 @@ public class GameContext implements Runnable {
|
||||
|
||||
// Daily
|
||||
private long epochDays;
|
||||
private int epochWeeks;
|
||||
|
||||
public GameContext() {
|
||||
this.sessions = new Object2ObjectOpenHashMap<>();
|
||||
@@ -42,6 +45,7 @@ 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);
|
||||
|
||||
// Run game loop
|
||||
@@ -77,7 +81,9 @@ public class GameContext implements Runnable {
|
||||
// TODO add timeout to config
|
||||
public synchronized void cleanupInactiveSessions() {
|
||||
var it = this.getSessions().entrySet().iterator();
|
||||
long timeout = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(600); // 10 minutes
|
||||
|
||||
int time = Nebula.getConfig().getServerOptions().sessionTimeout;
|
||||
long timeout = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(time);
|
||||
|
||||
while (it.hasNext()) {
|
||||
var session = it.next().getValue();
|
||||
@@ -101,6 +107,7 @@ public class GameContext implements Runnable {
|
||||
|
||||
long lastEpochDays = this.epochDays;
|
||||
this.epochDays = date.toEpochDay();
|
||||
this.epochWeeks = Utils.getWeeks(this.epochDays);
|
||||
|
||||
// Check if the day was changed
|
||||
if (this.epochDays > lastEpochDays) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
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 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")) {
|
||||
incrementalAchievementSet.add(condition.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
incrementalAchievementSet.remove(AchievementCondition.AchievementTotal.getValue());
|
||||
|
||||
incrementalAchievementSet.add(AchievementCondition.ItemsAdd.getValue());
|
||||
incrementalAchievementSet.add(AchievementCondition.ItemsDeplete.getValue());
|
||||
|
||||
// Fix params
|
||||
fixParams();
|
||||
}
|
||||
|
||||
private static void fixParams() {
|
||||
// Monolith
|
||||
addParam(78, 0, 2);
|
||||
addParam(79, 0, 4);
|
||||
addParam(498, 0, 1);
|
||||
|
||||
// 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
|
||||
addParam(393, 1, 0);
|
||||
addParam(394, 1, 0);
|
||||
addParam(395, 1, 0);
|
||||
addParam(396, 1, 0);
|
||||
addParam(397, 1, 0);
|
||||
addParam(398, 1, 0);
|
||||
|
||||
// Disc count
|
||||
addParam(382, 1, 0);
|
||||
addParam(383, 1, 0);
|
||||
addParam(384, 1, 0);
|
||||
addParam(385, 1, 0);
|
||||
addParam(386, 1, 0);
|
||||
addParam(387, 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
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 lombok.Setter;
|
||||
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;
|
||||
|
||||
@Setter
|
||||
private transient boolean queueSave;
|
||||
|
||||
@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) {
|
||||
//
|
||||
boolean hasCompleted = false;
|
||||
|
||||
// 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);
|
||||
|
||||
// Only save/update on client if achievement was changed
|
||||
if (changed) {
|
||||
// Sync
|
||||
this.syncAchievement(achievement);
|
||||
|
||||
// Set save flag
|
||||
this.queueSave = true;
|
||||
|
||||
// Check if achievement was completed
|
||||
if (achievement.isComplete()) {
|
||||
hasCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger update
|
||||
if (hasCompleted) {
|
||||
this.getPlayer().trigger(AchievementCondition.AchievementTotal, this.getCompletedAchievementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
boolean hasCompleted = false;
|
||||
|
||||
// Parse achievements
|
||||
for (var data : triggerList) {
|
||||
// Get achievement
|
||||
var achievement = this.getAchievement(data);
|
||||
|
||||
// Update achievement
|
||||
boolean changed = achievement.trigger(isTotal, progress, param1, param2);
|
||||
|
||||
// Only save/update on client if achievement was changed
|
||||
if (changed) {
|
||||
// Sync
|
||||
this.syncAchievement(achievement);
|
||||
|
||||
// Set save flag
|
||||
this.queueSave = true;
|
||||
|
||||
// Check if achievement was completed
|
||||
if (achievement.isComplete()) {
|
||||
hasCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger update
|
||||
if (hasCompleted) {
|
||||
this.getPlayer().trigger(AchievementCondition.AchievementTotal, this.getCompletedAchievementsCount());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this achievement on the player client
|
||||
*/
|
||||
private void syncAchievement(GameAchievement achievement) {
|
||||
if (!getPlayer().hasSession()) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
127
src/main/java/emu/nebula/game/activity/ActivityManager.java
Normal file
127
src/main/java/emu/nebula/game/activity/ActivityManager.java
Normal file
@@ -0,0 +1,127 @@
|
||||
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;
|
||||
}
|
||||
|
||||
GameActivity activity = switch (data.getType()) {
|
||||
case Trial -> new TrialActivity(this, data);
|
||||
default -> null;
|
||||
};
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
Nebula.getGameDatabase().save(this);
|
||||
this.queueSave = false;
|
||||
}
|
||||
}
|
||||
27
src/main/java/emu/nebula/game/activity/ActivityModule.java
Normal file
27
src/main/java/emu/nebula/game/activity/ActivityModule.java
Normal file
@@ -0,0 +1,27 @@
|
||||
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
|
||||
this.activities.add(700102);
|
||||
this.activities.add(700103);
|
||||
this.activities.add(700104);
|
||||
this.activities.add(700107);
|
||||
|
||||
//this.activities.add(101002);
|
||||
//this.activities.add(101003);
|
||||
}
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -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,10 +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;
|
||||
|
||||
@@ -58,13 +60,23 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
|
||||
// Make sure we own the characters
|
||||
var characters = new ArrayList<GameCharacter>();
|
||||
|
||||
for (int charId : charIds) {
|
||||
if (!getPlayer().getCharacters().hasCharacter(charId)) {
|
||||
var character = getPlayer().getCharacters().getCharacterById(charId);
|
||||
|
||||
// Also check if character fits the commission level requirement
|
||||
if (character == null || character.getLevel() < data.getLevel()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
characters.add(character);
|
||||
}
|
||||
|
||||
// TODO verify char tags for rewards
|
||||
// Check char tags
|
||||
if (!data.hasTags(characters)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create agent
|
||||
var agent = new Agent(data, processTime, charIds.toArray());
|
||||
@@ -73,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;
|
||||
@@ -139,12 +151,30 @@ public class AgentManager extends PlayerManager implements GameDatabaseObject {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create rewards
|
||||
var rewards = duration.getRewards().generate();
|
||||
result.setRewards(rewards);
|
||||
// Check if we had extra tags
|
||||
var characters = new ArrayList<GameCharacter>();
|
||||
|
||||
// Add to inventory
|
||||
this.getPlayer().getInventory().addItems(rewards, change);
|
||||
for (int charId : agent.getCharIds()) {
|
||||
var character = getPlayer().getCharacters().getCharacterById(charId);
|
||||
if (character == null) continue;
|
||||
|
||||
characters.add(character);
|
||||
}
|
||||
|
||||
// Create rewards
|
||||
result.setRewards(duration.getRewards().generate());
|
||||
|
||||
// Add rewards to inventory
|
||||
this.getPlayer().getInventory().addItems(result.getRewards(), change);
|
||||
|
||||
// Add bonus rewards if we meet the requirements
|
||||
if (data.hasExtraTags(characters)) {
|
||||
// Get bonus rewards
|
||||
result.setBonus(duration.getBonus().generate());
|
||||
|
||||
// Add rewards to inventory
|
||||
this.getPlayer().getInventory().addItems(result.getBonus(), change);
|
||||
}
|
||||
}
|
||||
|
||||
// Set results in change info
|
||||
@@ -153,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);
|
||||
|
||||
@@ -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,19 +8,29 @@ 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;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
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;
|
||||
|
||||
public CharacterStorage(Player player) {
|
||||
super(player);
|
||||
|
||||
@@ -65,6 +75,12 @@ public class CharacterStorage extends PlayerManager {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -72,18 +88,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();
|
||||
|
||||
@@ -140,6 +144,12 @@ public class CharacterStorage extends PlayerManager {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -205,6 +215,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,13 +18,15 @@ 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;
|
||||
import emu.nebula.proto.Public.AffinityInfo;
|
||||
import emu.nebula.proto.Public.Char;
|
||||
import emu.nebula.proto.Public.CharGemPreset;
|
||||
import emu.nebula.proto.Public.CharGemSlot;
|
||||
@@ -35,6 +37,7 @@ import emu.nebula.util.Bitset;
|
||||
import emu.nebula.util.CustomIntArray;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.Getter;
|
||||
import us.hebi.quickbuf.RepeatedInt;
|
||||
|
||||
@@ -53,9 +56,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
private int advance;
|
||||
private int level;
|
||||
private int exp;
|
||||
private int affinityLevel;
|
||||
private int affinityExp;
|
||||
private int skin;
|
||||
private int[] skills;
|
||||
private Bitset talents;
|
||||
private IntSet plots;
|
||||
private long createTime;
|
||||
|
||||
private int gemPresetIndex;
|
||||
@@ -112,6 +118,10 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaster() {
|
||||
return this.getData().getGrade() == 1;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
@@ -119,7 +129,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
public void setAdvance(int advance) {
|
||||
this.advance = advance;
|
||||
}
|
||||
|
||||
|
||||
public void setAffinityLevel(int level) {
|
||||
this.affinityLevel = Math.max(level, 0);
|
||||
this.affinityExp = 0;
|
||||
}
|
||||
|
||||
public int getMaxGainableExp() {
|
||||
if (this.getLevel() >= this.getMaxLevel()) {
|
||||
return 0;
|
||||
@@ -176,7 +191,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
|
||||
@@ -245,7 +260,7 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
// Set advance skin
|
||||
this.skin = this.getData().getAdvanceSkinId();
|
||||
|
||||
// Send packets
|
||||
// Add next packages
|
||||
this.getPlayer().addNextPackage(
|
||||
NetMsgId.character_skin_gain_notify,
|
||||
Skin.newInstance().setNew(UI32.newInstance().setValue(this.getSkin()))
|
||||
@@ -254,11 +269,17 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
NetMsgId.character_skin_change_notify,
|
||||
SkinChange.newInstance().setCharId(this.getCharId()).setSkinId(this.getSkin())
|
||||
);
|
||||
|
||||
// Set flag for player to update character skins in their handbook
|
||||
this.getPlayer().getCharacters().setUpdateCharHandbook(true);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(AchievementCondition.CharacterAdvanceTotal, 1);
|
||||
|
||||
// Success
|
||||
return changes.setSuccess(true);
|
||||
}
|
||||
@@ -375,6 +396,121 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Affinity
|
||||
|
||||
public int getMaxAffinityExp() {
|
||||
var data = GameData.getAffinityLevelDataTable().get(this.affinityLevel + 1);
|
||||
return data != null ? data.getNeedExp() : 0;
|
||||
}
|
||||
|
||||
public void addAffinityExp(int amount) {
|
||||
// Setup
|
||||
int expRequired = this.getMaxAffinityExp();
|
||||
|
||||
// Add exp
|
||||
this.affinityExp += amount;
|
||||
|
||||
// Check for level ups
|
||||
while (this.affinityExp >= expRequired && expRequired > 0) {
|
||||
this.affinityLevel += 1;
|
||||
this.affinityExp -= expRequired;
|
||||
|
||||
expRequired = this.getMaxAffinityExp();
|
||||
}
|
||||
|
||||
// Clamp exp
|
||||
if (expRequired <= 0) {
|
||||
this.affinityExp = 0;
|
||||
}
|
||||
|
||||
// Save to database
|
||||
this.save();
|
||||
}
|
||||
|
||||
public PlayerChangeInfo sendGift(ItemParamMap items) {
|
||||
// Verify that the player has the items
|
||||
if (!this.getPlayer().getInventory().hasItems(items)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Caluclate amount of exp to gain
|
||||
var charDescription = this.getData().getDes();
|
||||
int exp = 0;
|
||||
int count = 0;
|
||||
|
||||
for (var item : items) {
|
||||
// Get data
|
||||
var gift = GameData.getAffinityGiftDataTable().get(item.getIntKey());
|
||||
if (gift == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculate amount
|
||||
double amount = gift.getBaseAffinity() * item.getIntValue();
|
||||
|
||||
if (charDescription.isPreferGift(gift)) {
|
||||
amount = amount * 1.5D;
|
||||
} else if (charDescription.isHateGift(gift)) {
|
||||
amount = amount * 0.8D;
|
||||
}
|
||||
|
||||
// Add
|
||||
exp += (int) amount;
|
||||
count += item.getIntValue();
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (exp <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add affinity exp
|
||||
this.addAffinityExp(exp);
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(QuestCondition.GiftGiveTotal, count);
|
||||
|
||||
// Remove items
|
||||
var change = this.getPlayer().getInventory().removeItems(items);
|
||||
|
||||
// Success
|
||||
return change;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo recvPlotReward(int plotId) {
|
||||
// Create change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Sanity check to prevent players from recving rewards over and over again from the same plot
|
||||
if (this.getPlots() != null && this.getPlots().contains(plotId)) {
|
||||
return change;
|
||||
}
|
||||
|
||||
// Get data
|
||||
var plot = GameData.getPlotDataTable().get(plotId);
|
||||
|
||||
// Sanity check to make sure we can complete this plot quest
|
||||
if (plot == null || plot.getChar() != this.getCharId() || plot.getUnlockAffinityLevel() > this.getAffinityLevel()) {
|
||||
return change;
|
||||
}
|
||||
|
||||
// Complete plot
|
||||
if (this.plots == null) {
|
||||
this.plots = new IntOpenHashSet();
|
||||
}
|
||||
|
||||
this.getPlots().add(plotId);
|
||||
|
||||
// Update to database
|
||||
this.save();
|
||||
|
||||
// Add items
|
||||
this.getPlayer().getInventory().addItems(plot.getRewards(), change);
|
||||
|
||||
// Success
|
||||
return change;
|
||||
}
|
||||
|
||||
// Gems
|
||||
|
||||
public boolean hasGemPreset(int index) {
|
||||
@@ -659,6 +795,8 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
.setLevel(this.getLevel())
|
||||
.setSkin(this.getSkin())
|
||||
.setAdvance(this.getAdvance())
|
||||
.setAffinityLevel(this.getAffinityLevel())
|
||||
.setAffinityExp(this.getAffinityExp())
|
||||
.setTalentNodes(this.getTalents().toByteArray())
|
||||
.addAllSkillLvs(this.getSkills())
|
||||
.setCreateTime(this.getCreateTime());
|
||||
@@ -694,6 +832,12 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
// Affinity quests
|
||||
proto.getMutableAffinityQuests();
|
||||
|
||||
// Encode plots
|
||||
if (this.getPlots() != null) {
|
||||
this.getPlots().forEach(proto::addPlots);
|
||||
}
|
||||
|
||||
// Finish
|
||||
return proto;
|
||||
}
|
||||
|
||||
@@ -702,6 +846,7 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
.setId(this.getCharId())
|
||||
.setAdvance(this.getAdvance())
|
||||
.setLevel(this.getLevel())
|
||||
.setAffinityLevel(this.getAffinityLevel())
|
||||
.setTalentNodes(this.getTalents().toByteArray())
|
||||
.addAllSkillLvs(this.getSkills());
|
||||
|
||||
@@ -725,6 +870,15 @@ public class GameCharacter implements GameDatabaseObject {
|
||||
return proto;
|
||||
}
|
||||
|
||||
public AffinityInfo getAffinityProto() {
|
||||
var proto = AffinityInfo.newInstance()
|
||||
.setCharId(this.getCharId())
|
||||
.setAffinityLevel(this.getAffinityLevel())
|
||||
.setAffinityExp(this.getAffinityExp());
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Database fix
|
||||
|
||||
@PreLoad
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
7
src/main/java/emu/nebula/game/dating/DatingEvent.java
Normal file
7
src/main/java/emu/nebula/game/dating/DatingEvent.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package emu.nebula.game.dating;
|
||||
|
||||
public interface DatingEvent {
|
||||
|
||||
public int getId();
|
||||
|
||||
}
|
||||
35
src/main/java/emu/nebula/game/dating/DatingEventType.java
Normal file
35
src/main/java/emu/nebula/game/dating/DatingEventType.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package emu.nebula.game.dating;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum DatingEventType {
|
||||
Start (1),
|
||||
End (2),
|
||||
Landmark (3),
|
||||
Regular (4),
|
||||
LimitedLandmark (5),
|
||||
BranchA (6),
|
||||
BranchB (7),
|
||||
BeforeBranch (8),
|
||||
AfterBranch (9);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<DatingEventType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (DatingEventType type : DatingEventType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private DatingEventType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static DatingEventType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
31
src/main/java/emu/nebula/game/dating/DatingGame.java
Normal file
31
src/main/java/emu/nebula/game/dating/DatingGame.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package emu.nebula.game.dating;
|
||||
|
||||
import emu.nebula.data.resources.DatingLandmarkDef;
|
||||
import emu.nebula.game.character.GameCharacter;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class DatingGame {
|
||||
private GameCharacter character;
|
||||
private DatingLandmarkDef landmark;
|
||||
|
||||
private int[] branchOptionsA;
|
||||
private int[] branchOptionsB;
|
||||
|
||||
public DatingGame(GameCharacter character, DatingLandmarkDef landmark) {
|
||||
this.character = character;
|
||||
this.landmark = landmark;
|
||||
this.branchOptionsA = new int[] {1, 2};
|
||||
this.branchOptionsB = new int[] {1, 2};
|
||||
}
|
||||
|
||||
public boolean selectDatingBranchA(int optionId) {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectDatingBranchB(int optionId) {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
39
src/main/java/emu/nebula/game/dating/DatingManager.java
Normal file
39
src/main/java/emu/nebula/game/dating/DatingManager.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package emu.nebula.game.dating;
|
||||
|
||||
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.QuestCondition;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class DatingManager extends PlayerManager {
|
||||
private DatingGame game;
|
||||
|
||||
public DatingManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public DatingGame selectLandmark(GameCharacter character, int landmarkId) {
|
||||
// Get landmark data
|
||||
var data = GameData.getDatingLandmarkDataTable().get(landmarkId);
|
||||
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set landmark + character
|
||||
this.game = new DatingGame(character, data);
|
||||
|
||||
// Trigger quest/achievement
|
||||
this.getPlayer().trigger(QuestCondition.CharactersDatingTotal, 1);
|
||||
|
||||
// Success
|
||||
return this.game;
|
||||
}
|
||||
|
||||
public void endDatingGame() {
|
||||
this.game = null;
|
||||
}
|
||||
}
|
||||
@@ -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,6 +2,7 @@ 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;
|
||||
@@ -75,6 +76,9 @@ public class InfinityTowerManager extends PlayerManager {
|
||||
// Log in player progress
|
||||
this.getPlayer().getProgress().addInfinityArenaLog(this.getLevelId());
|
||||
|
||||
// Trigger achievement
|
||||
this.getPlayer().trigger(AchievementCondition.InfinityTowerClearSpecificFloor, 10, this.getLevelId(), 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,8 +63,8 @@ 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
|
||||
@@ -74,7 +74,7 @@ public class InstanceManager extends PlayerManager {
|
||||
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 +131,8 @@ 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);
|
||||
|
||||
@@ -11,7 +11,7 @@ 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 +20,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;
|
||||
@@ -152,12 +153,15 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
// Save to database
|
||||
Nebula.getGameDatabase().addToSet(this, this.getUid(), "extraSkins", id);
|
||||
|
||||
// Send packet
|
||||
// Send packets
|
||||
this.getPlayer().addNextPackage(
|
||||
NetMsgId.character_skin_gain_notify,
|
||||
Skin.newInstance().setNew(UI32.newInstance().setValue(id))
|
||||
);
|
||||
|
||||
// Set flag for player to update character skins in their handbook
|
||||
this.getPlayer().getCharacters().setUpdateCharHandbook(true);
|
||||
|
||||
// Success
|
||||
return true;
|
||||
}
|
||||
@@ -459,11 +463,11 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger quest
|
||||
// Trigger quest + achievement
|
||||
if (amount > 0) {
|
||||
this.getPlayer().triggerQuest(QuestCondType.ItemsAdd, amount, id);
|
||||
this.getPlayer().trigger(QuestCondition.ItemsAdd, amount, id);
|
||||
} else {
|
||||
this.getPlayer().triggerQuest(QuestCondType.ItemsDeplete, Math.abs(amount), id);
|
||||
this.getPlayer().trigger(QuestCondition.ItemsDeplete, Math.abs(amount), id);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -631,24 +635,34 @@ 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);
|
||||
}
|
||||
|
||||
public PlayerChangeInfo buyEnergy() {
|
||||
public PlayerChangeInfo buyEnergy(int count) {
|
||||
// Validate count
|
||||
if (count <= 0 || count > 6) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Make sure we have the gems
|
||||
if (!this.hasItem(GameConstants.ENERGY_BUY_ITEM_ID, 30)) {
|
||||
int cost = 30 * count;
|
||||
|
||||
if (!this.hasItem(GameConstants.ENERGY_BUY_ITEM_ID, cost)) {
|
||||
return change;
|
||||
}
|
||||
|
||||
// Remove gems
|
||||
this.removeItem(GameConstants.ENERGY_BUY_ITEM_ID, 30, change);
|
||||
this.removeItem(GameConstants.ENERGY_BUY_ITEM_ID, cost, change);
|
||||
|
||||
// Add energy
|
||||
this.getPlayer().addEnergy(60, change);
|
||||
this.getPlayer().addEnergy(60 * count, change);
|
||||
|
||||
// Success
|
||||
return change;
|
||||
@@ -768,7 +782,7 @@ public class Inventory extends PlayerManager implements GameDatabaseObject {
|
||||
}
|
||||
case 3 -> {
|
||||
// Selected item
|
||||
int selectCount = data.getUseParams().get(selectId);
|
||||
int selectCount = data.getUseParams().get(selectId) * count;
|
||||
|
||||
if (selectCount <= 0) {
|
||||
return change;
|
||||
|
||||
@@ -2,6 +2,7 @@ package emu.nebula.game.player;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import dev.morphia.annotations.AlsoLoad;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
@@ -11,9 +12,13 @@ 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;
|
||||
import emu.nebula.game.dating.DatingManager;
|
||||
import emu.nebula.game.formation.FormationManager;
|
||||
import emu.nebula.game.friends.FriendList;
|
||||
import emu.nebula.game.gacha.GachaManager;
|
||||
@@ -21,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;
|
||||
@@ -42,6 +47,7 @@ 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;
|
||||
import emu.nebula.proto.Public.Title;
|
||||
|
||||
import lombok.Getter;
|
||||
@@ -57,6 +63,10 @@ public class Player implements GameDatabaseObject {
|
||||
private transient Account account;
|
||||
private transient GameSession session;
|
||||
|
||||
@Indexed
|
||||
@AlsoLoad("playerRemoteToken")
|
||||
private String remoteToken;
|
||||
|
||||
// Details
|
||||
private String name;
|
||||
private String signature;
|
||||
@@ -82,6 +92,7 @@ public class Player implements GameDatabaseObject {
|
||||
private final transient CharacterStorage characters;
|
||||
private final transient FriendList friendList;
|
||||
private final transient BattlePassManager battlePassManager;
|
||||
private final transient DatingManager datingManager;
|
||||
private final transient StarTowerManager starTowerManager;
|
||||
private final transient InstanceManager instanceManager;
|
||||
private final transient InfinityTowerManager infinityTowerManager;
|
||||
@@ -96,7 +107,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;
|
||||
@@ -108,6 +121,7 @@ public class Player implements GameDatabaseObject {
|
||||
this.characters = new CharacterStorage(this);
|
||||
this.friendList = new FriendList(this);
|
||||
this.battlePassManager = new BattlePassManager(this);
|
||||
this.datingManager = new DatingManager(this);
|
||||
this.starTowerManager = new StarTowerManager(this);
|
||||
this.instanceManager = new InstanceManager(this);
|
||||
this.infinityTowerManager = new InfinityTowerManager(this);
|
||||
@@ -193,6 +207,41 @@ public class Player implements GameDatabaseObject {
|
||||
public boolean hasSession() {
|
||||
return this.session != null;
|
||||
}
|
||||
|
||||
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) {
|
||||
this.exp = exp;
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "exp", this.exp);
|
||||
}
|
||||
|
||||
public void setRemoteToken(String token) {
|
||||
// Skip if tokens are the same
|
||||
if (this.remoteToken == null) {
|
||||
if (token == null) {
|
||||
return;
|
||||
}
|
||||
} else if (this.remoteToken != null) {
|
||||
if (this.remoteToken.equals(token)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Set remote token
|
||||
this.remoteToken = token;
|
||||
|
||||
// Update in database
|
||||
Nebula.getGameDatabase().update(this, this.getUid(), "remoteToken", this.remoteToken);
|
||||
}
|
||||
|
||||
public boolean getGender() {
|
||||
return this.gender;
|
||||
@@ -443,16 +492,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(
|
||||
@@ -460,6 +510,9 @@ public class Player implements GameDatabaseObject {
|
||||
WorldClassRewardState.newInstance()
|
||||
.setFlag(getQuestManager().getLevelRewards().toBigEndianByteArray())
|
||||
);
|
||||
|
||||
// Trigger achievement
|
||||
this.trigger(AchievementCondition.WorldClassSpecific, this.getLevel());
|
||||
}
|
||||
|
||||
// Calculate changes
|
||||
@@ -512,7 +565,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;
|
||||
@@ -547,12 +600,6 @@ public class Player implements GameDatabaseObject {
|
||||
return change;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
public void sendMessage(String string) {
|
||||
// Empty
|
||||
}
|
||||
|
||||
// Dailies
|
||||
|
||||
public void checkResetDailies() {
|
||||
@@ -561,8 +608,16 @@ public class Player implements GameDatabaseObject {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if week has changed (Resets on monday)
|
||||
// TODO add a config option
|
||||
int curWeek = Utils.getWeeks(this.getLastEpochDay());
|
||||
boolean hasWeekChanged = Nebula.getGameContext().getEpochWeeks() > curWeek;
|
||||
|
||||
// Reset dailies
|
||||
this.resetDailies(false);
|
||||
this.resetDailies(hasWeekChanged);
|
||||
|
||||
// Trigger quest/achievement login
|
||||
this.trigger(QuestCondition.LoginTotal, 1);
|
||||
|
||||
// Update last epoch day
|
||||
this.lastEpochDay = Nebula.getGameContext().getEpochDays();
|
||||
@@ -575,15 +630,28 @@ public class Player implements GameDatabaseObject {
|
||||
this.getBattlePassManager().getBattlePass().resetDailyQuests(resetWeekly);
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -627,7 +695,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) {
|
||||
@@ -635,6 +705,9 @@ public class Player implements GameDatabaseObject {
|
||||
this.save();
|
||||
}
|
||||
|
||||
// Init activities
|
||||
this.getActivityManager().init();
|
||||
|
||||
// Load complete
|
||||
this.loaded = true;
|
||||
}
|
||||
@@ -643,9 +716,6 @@ public class Player implements GameDatabaseObject {
|
||||
// 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());
|
||||
@@ -661,6 +731,18 @@ 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() {
|
||||
@@ -739,18 +821,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()
|
||||
@@ -831,6 +915,11 @@ public class Player implements GameDatabaseObject {
|
||||
agentProto.addInfos(agent.toProto());
|
||||
}
|
||||
|
||||
// Activities
|
||||
for (var activity : getActivityManager().getActivities().values()) {
|
||||
proto.addActivities(activity.toProto());
|
||||
}
|
||||
|
||||
// Complete
|
||||
return proto;
|
||||
}
|
||||
@@ -877,4 +966,4 @@ public class Player implements GameDatabaseObject {
|
||||
return proto;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -135,8 +135,6 @@ public class PlayerModule extends GameContextModule {
|
||||
|
||||
// Put in player cache
|
||||
this.addToCache(player);
|
||||
|
||||
System.out.println("created player");
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ public class PlayerProgress extends PlayerManager implements GameDatabaseObject
|
||||
}
|
||||
|
||||
// Set
|
||||
this.starTowerGrowth[index] |= (1 << nodeId);
|
||||
this.starTowerGrowth[index] |= (1 << (nodeId - 1));
|
||||
|
||||
// Success
|
||||
return true;
|
||||
|
||||
@@ -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) {
|
||||
@@ -194,7 +194,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 +305,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);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class ScoreBossManager extends PlayerManager {
|
||||
}
|
||||
|
||||
public int getControlId() {
|
||||
return 1;
|
||||
return Nebula.getGameContext().getScoreBossModule().getControlId();
|
||||
}
|
||||
|
||||
public ScoreBossControlDef getControlData() {
|
||||
@@ -79,7 +79,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);
|
||||
|
||||
// Save ranking
|
||||
this.ranking.save();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,10 +60,16 @@ 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) {
|
||||
// Update player data
|
||||
this.update(player);
|
||||
|
||||
// Reset score entry if control id doesn't match
|
||||
if (this.controlId != controlId) {
|
||||
this.controlId = controlId;
|
||||
this.getTeams().clear();
|
||||
}
|
||||
|
||||
// Set team entry
|
||||
var team = new ScoreBossTeamEntry(player, build, stars, score);
|
||||
this.getTeams().put(level, team);
|
||||
|
||||
@@ -38,6 +38,18 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public boolean hasNew() {
|
||||
if (this.getCompletedStories().size() < GameData.getStoryDataTable().size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.getCompletedSets().size() < GameData.getStorySetSectionDataTable().size()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public PlayerChangeInfo settle(IntList list) {
|
||||
// Player change info
|
||||
@@ -63,6 +75,7 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
Nebula.getGameDatabase().addToSet(this, this.getPlayerUid(), "completedStories", id);
|
||||
}
|
||||
|
||||
// Complete
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -90,6 +103,7 @@ public class StoryManager extends PlayerManager implements GameDatabaseObject {
|
||||
// Save to db
|
||||
Nebula.getGameDatabase().update(this, this.getPlayerUid(), "completedSets." + chapterId, sectionIndex);
|
||||
|
||||
// Complete
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,30 +44,29 @@ 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()) {
|
||||
@@ -84,8 +90,23 @@ public class StarTowerBuild implements GameDatabaseObject {
|
||||
this.getSubNoteSkills().put(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 +130,38 @@ public class StarTowerBuild implements GameDatabaseObject {
|
||||
|
||||
// Score
|
||||
|
||||
private int calculateScore() {
|
||||
// Init score
|
||||
int score = 0;
|
||||
public int calculateScore() {
|
||||
// Clear score
|
||||
this.score = 0;
|
||||
|
||||
// Potentials
|
||||
// Add score from potentials
|
||||
for (var potential : this.getPotentials().int2IntEntrySet()) {
|
||||
var data = GameData.getPotentialDataTable().get(potential.getIntKey());
|
||||
if (data == null) continue;
|
||||
|
||||
int index = potential.getIntValue() - 1;
|
||||
score += data.getBuildScore()[index];
|
||||
this.score += data.getBuildScore(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 +216,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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,11 +3,12 @@ package emu.nebula.game.tower;
|
||||
import emu.nebula.Nebula;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.StarTowerGrowthNodeDef;
|
||||
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 emu.nebula.game.quest.QuestCondType;
|
||||
import emu.nebula.game.quest.QuestCondition;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
@@ -55,7 +56,7 @@ public class StarTowerManager extends PlayerManager {
|
||||
|
||||
// Get nodes bits
|
||||
int nodes = growth[groupIndex];
|
||||
int test = (1 << data.getNodeId());
|
||||
int test = (1 << (data.getNodeId() - 1));
|
||||
|
||||
// Check if bit is set
|
||||
return (nodes & test) != 0;
|
||||
@@ -117,7 +118,7 @@ public class StarTowerManager extends PlayerManager {
|
||||
this.getProgress().setStarTowerGrowthNode(data.getGroup(), data.getNodeId());
|
||||
|
||||
// Remove items
|
||||
getPlayer().getInventory().removeItem(data.getItemId1(), data.getItemQty1());
|
||||
getPlayer().getInventory().removeItem(data.getItemId1(), data.getItemQty1(), change);
|
||||
|
||||
// Add to unlocked list
|
||||
unlocked.add(data.getId());
|
||||
@@ -204,27 +205,53 @@ public class StarTowerManager extends PlayerManager {
|
||||
}
|
||||
|
||||
// Create game
|
||||
this.game = new StarTowerGame(this, data, formation, req);
|
||||
try {
|
||||
this.game = new StarTowerGame(this, data, formation, req);
|
||||
} catch (Exception e) {
|
||||
Nebula.getLogger().error("Could not create star tower game", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trigger quest
|
||||
this.getPlayer().triggerQuest(QuestCondType.TowerEnterFloor, 1);
|
||||
this.getPlayer().trigger(QuestCondition.TowerEnterFloor, 1);
|
||||
|
||||
// Success
|
||||
return change.setExtraData(this.game);
|
||||
}
|
||||
|
||||
public StarTowerGame endGame() {
|
||||
public StarTowerGame endGame(boolean victory) {
|
||||
// Cache instance
|
||||
var game = this.game;
|
||||
|
||||
if (game != null) {
|
||||
// Set last build
|
||||
this.lastBuild = game.getBuild();
|
||||
|
||||
// Clear instance
|
||||
this.game = null;
|
||||
if (game == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set last build
|
||||
this.lastBuild = game.getBuild();
|
||||
|
||||
// Handle victory events
|
||||
if (victory) {
|
||||
// Trigger achievements
|
||||
this.getPlayer().trigger(AchievementCondition.TowerClearTotal, 1);
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.TowerClearSpecificGroupIdAndDifficulty,
|
||||
1,
|
||||
game.getData().getGroupId(),
|
||||
game.getData().getDifficulty()
|
||||
);
|
||||
this.getPlayer().trigger(
|
||||
AchievementCondition.TowerClearSpecificLevelWithDifficultyAndTotal,
|
||||
1,
|
||||
game.getData().getId(),
|
||||
game.getData().getDifficulty()
|
||||
);
|
||||
}
|
||||
|
||||
// Clear game instance
|
||||
this.game = null;
|
||||
|
||||
// Return game
|
||||
return game;
|
||||
}
|
||||
|
||||
@@ -300,9 +327,18 @@ public class StarTowerManager extends PlayerManager {
|
||||
// Database
|
||||
|
||||
public void loadFromDatabase() {
|
||||
// Init builds
|
||||
this.builds = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
// Load builds with the current player's uid
|
||||
Nebula.getGameDatabase().getObjects(StarTowerBuild.class, "playerUid", getPlayerUid()).forEach(build -> {
|
||||
// Fix outdated builds
|
||||
if (build.getSecondarySkills() == null) {
|
||||
build.calculateScore();
|
||||
build.save();
|
||||
}
|
||||
|
||||
// Add build
|
||||
this.builds.put(build.getUid(), build);
|
||||
});
|
||||
}
|
||||
|
||||
181
src/main/java/emu/nebula/game/tower/StarTowerModifiers.java
Normal file
181
src/main/java/emu/nebula/game/tower/StarTowerModifiers.java
Normal file
@@ -0,0 +1,181 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Data class to hold various modifiers for star tower.
|
||||
*/
|
||||
@Getter
|
||||
public class StarTowerModifiers {
|
||||
private StarTowerGame game;
|
||||
|
||||
// Strengthen machines
|
||||
private boolean enableEndStrengthen;
|
||||
private boolean enableShopStrengthen;
|
||||
|
||||
private boolean freeStrengthen;
|
||||
private int strengthenDiscount;
|
||||
|
||||
// Bonus max potential level
|
||||
private int bonusMaxPotentialLevel;
|
||||
|
||||
// Shop
|
||||
private int shopGoodsCount;
|
||||
|
||||
private int shopRerollCount;
|
||||
private int shopRerollPrice;
|
||||
|
||||
private boolean shopDiscountTier1;
|
||||
private boolean shopDiscountTier2;
|
||||
private boolean shopDiscountTier3;
|
||||
|
||||
// Bonus potential level proc
|
||||
private double bonusStrengthenChance = 0;
|
||||
private double bonusPotentialChance = 0;
|
||||
private int bonusPotentialLevel = 0;
|
||||
|
||||
private int potentialRerollCount;
|
||||
private int potentialRerollDiscount;
|
||||
|
||||
public StarTowerModifiers(StarTowerGame game) {
|
||||
this.game = game;
|
||||
|
||||
// Strengthen machines
|
||||
this.enableEndStrengthen = game.getDifficulty() >= 2 && this.hasGrowthNode(10601);
|
||||
this.enableShopStrengthen = game.getDifficulty() >= 4 && this.hasGrowthNode(20301);
|
||||
|
||||
this.freeStrengthen = this.hasGrowthNode(10801);
|
||||
|
||||
// Strengthen discount (Set Meal Agreement)
|
||||
if (this.hasGrowthNode(30402)) {
|
||||
this.strengthenDiscount = 60;
|
||||
} else if (this.hasGrowthNode(30102)) {
|
||||
this.strengthenDiscount = 30;
|
||||
}
|
||||
|
||||
// Bonus potential max level (Ocean of Souls)
|
||||
if (this.hasGrowthNode(30301)) {
|
||||
this.bonusMaxPotentialLevel = 6;
|
||||
} else if (this.hasGrowthNode(20601)) {
|
||||
this.bonusMaxPotentialLevel = 4;
|
||||
}
|
||||
|
||||
// Shop extra goods (Monolith Premium)
|
||||
if (this.hasGrowthNode(20702)) {
|
||||
this.shopGoodsCount = 8;
|
||||
} else if (this.hasGrowthNode(20402)) {
|
||||
this.shopGoodsCount = 6;
|
||||
} else if (this.hasGrowthNode(10402)) {
|
||||
this.shopGoodsCount = 4;
|
||||
} else {
|
||||
this.shopGoodsCount = 2;
|
||||
}
|
||||
|
||||
if (this.hasGrowthNode(20902)) {
|
||||
this.shopRerollCount++;
|
||||
}
|
||||
if (this.hasGrowthNode(30601)) {
|
||||
this.shopRerollCount++;
|
||||
}
|
||||
|
||||
if (this.shopRerollCount > 0) {
|
||||
this.shopRerollPrice = 100;
|
||||
}
|
||||
|
||||
// Shop discount (Member Discount)
|
||||
this.shopDiscountTier1 = game.getDifficulty() >= 3 && this.hasGrowthNode(20202);
|
||||
this.shopDiscountTier2 = game.getDifficulty() >= 4 && this.hasGrowthNode(20502);
|
||||
this.shopDiscountTier3 = game.getDifficulty() >= 5 && this.hasGrowthNode(20802);
|
||||
|
||||
// Bonus potential enhancement level procs (Potential Boost)
|
||||
if (game.getDifficulty() >= 7 && this.hasGrowthNode(30802)) {
|
||||
this.bonusStrengthenChance = 0.3;
|
||||
} else if (game.getDifficulty() >= 6 && this.hasGrowthNode(30502)) {
|
||||
this.bonusStrengthenChance = 0.2;
|
||||
} else if (game.getDifficulty() >= 6 && this.hasGrowthNode(30202)) {
|
||||
this.bonusStrengthenChance = 0.1;
|
||||
}
|
||||
|
||||
// Bonus potential levels (Butterflies Inside)
|
||||
if (game.getDifficulty() >= 7 && this.hasGrowthNode(30901)) {
|
||||
this.bonusPotentialChance = 0.3;
|
||||
this.bonusMaxPotentialLevel = 2;
|
||||
} else if (game.getDifficulty() >= 7 && this.hasGrowthNode(30801)) {
|
||||
this.bonusPotentialChance = 0.2;
|
||||
this.bonusMaxPotentialLevel = 1;
|
||||
} else if (game.getDifficulty() >= 6 && this.hasGrowthNode(30201)) {
|
||||
this.bonusPotentialChance = 0.1;
|
||||
this.bonusMaxPotentialLevel = 1;
|
||||
} else if (game.getDifficulty() >= 5 && this.hasGrowthNode(20801)) {
|
||||
this.bonusPotentialChance = 0.05;
|
||||
this.bonusMaxPotentialLevel = 1;
|
||||
}
|
||||
|
||||
// Potential reroll (Cloud Dice)
|
||||
if (this.hasGrowthNode(20901)) {
|
||||
this.potentialRerollCount += 1;
|
||||
}
|
||||
|
||||
// Potential reroll price discount (Destiny of Stars)
|
||||
if (this.hasGrowthNode(30702)) {
|
||||
this.potentialRerollDiscount = 60;
|
||||
} else if (this.hasGrowthNode(30401)) {
|
||||
this.potentialRerollDiscount = 40;
|
||||
} else if (this.hasGrowthNode(30101)) {
|
||||
this.potentialRerollDiscount = 30;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasGrowthNode(int nodeId) {
|
||||
return this.getGame().getManager().hasGrowthNode(nodeId);
|
||||
}
|
||||
|
||||
public int getStartingCoin() {
|
||||
int coin = 0;
|
||||
|
||||
if (this.hasGrowthNode(10103)) {
|
||||
coin += 50;
|
||||
} if (this.hasGrowthNode(10403)) {
|
||||
coin += 100;
|
||||
} if (this.hasGrowthNode(10702)) {
|
||||
coin += 200;
|
||||
}
|
||||
|
||||
return coin;
|
||||
}
|
||||
|
||||
public int getStartingSubNotes() {
|
||||
int subNotes = 0;
|
||||
|
||||
if (this.hasGrowthNode(10102)) {
|
||||
subNotes += 3;
|
||||
}
|
||||
|
||||
return subNotes;
|
||||
}
|
||||
|
||||
public void addStartingItems() {
|
||||
// Add starting coin directly
|
||||
int coin = this.getStartingCoin();
|
||||
if (coin > 0) {
|
||||
this.getGame().getRes().add(GameConstants.TOWER_COIN_ITEM_ID, coin);
|
||||
}
|
||||
|
||||
// Add starting subnotes
|
||||
int subNotes = this.getStartingSubNotes();
|
||||
|
||||
for (int i = 0; i < subNotes; i++) {
|
||||
int id = this.getGame().getRandomSubNoteId();
|
||||
this.getGame().getItems().add(id, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFreeStrengthen(boolean b) {
|
||||
this.freeStrengthen = b;
|
||||
}
|
||||
|
||||
public void consumeShopReroll() {
|
||||
this.shopRerollCount = Math.max(this.shopRerollCount - 1, 0);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user