mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-13 04:45:02 +01:00
Compare commits
35 Commits
v1.1.2
...
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 |
35
README.md
35
README.md
@@ -29,6 +29,14 @@ For any extra support, questions, or discussions, check out our [Discord](https:
|
||||
### Not implemented
|
||||
- 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,17 +57,24 @@ 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
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -68,14 +83,6 @@ class Handlers
|
||||
4. If `autoCreateAccount` is set to true in the config, then you can skip this step. Otherwise, type `/account create [account email]` in the server console to create an account.
|
||||
5. Login with your account email, the code field is ignored by the server and can be set to anything.
|
||||
|
||||
If you are not on the global client, `.stellasora.global` in the fiddlerscript may need to be changed to match the endpoint your client connects to.
|
||||
|
||||
### Supported regions
|
||||
|
||||
Nebula supports the global client by default. If you want to switch regions, you need to change the `customDataVersion` and `region` fields in the Nebula config. The `customDataVersion` field should match the the data version of your client, which is usually the last number of your client's version string (top left of your login screen). Example: 1.0.0.42 = data version 42.
|
||||
|
||||
Current supported regions: `global`, `kr`
|
||||
|
||||
### Server commands
|
||||
Server commands need to be run in the server console OR in the signature edit menu of your profile.
|
||||
|
||||
|
||||
@@ -6,8 +6,23 @@ import emu.nebula.game.inventory.ItemParam;
|
||||
import emu.nebula.util.WeightedList;
|
||||
|
||||
public class GameConstants {
|
||||
private static final int DATA_VERSION = 54;
|
||||
private static final String VERSION = "1.2.0";
|
||||
public static final String VERSION = "1.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;
|
||||
@@ -37,6 +51,14 @@ public class GameConstants {
|
||||
public static final int MAX_FRIENDSHIPS = 50;
|
||||
public static final int MAX_PENDING_FRIENDSHIPS = 30;
|
||||
|
||||
public static final int TOWER_COIN_ITEM_ID = 11;
|
||||
public static final int[] TOWER_COMMON_SUB_NOTE_SKILLS = new int[] {
|
||||
90011, 90012, 90013, 90014, 90015, 90016, 90017
|
||||
};
|
||||
public static final int[] TOWER_EVENTS_IDS = new int[] {
|
||||
101, 102, 104, 105, 106, 107, 108, 114, 115, 116, 126, 127, 128
|
||||
};
|
||||
|
||||
public static int[][] VAMPIRE_SURVIVOR_BONUS_POWER = new int[][] {
|
||||
new int[] {100, 120},
|
||||
new int[] {200, 150},
|
||||
@@ -57,7 +79,14 @@ public class GameConstants {
|
||||
// Helper functions
|
||||
|
||||
public static String getGameVersion() {
|
||||
return VERSION + "." + getDataVersion() + " (" + Nebula.getConfig().getRegion().toUpperCase() + ")";
|
||||
// Load data version
|
||||
var region = RegionConfig.getRegion(Nebula.getConfig().getRegion());
|
||||
|
||||
// Set data version from region
|
||||
GameConstants.DATA_VERSION = region.getDataVersion();
|
||||
|
||||
// Init game version string
|
||||
return VERSION + "." + getDataVersion() + " (" + region.getName().toUpperCase() + ")";
|
||||
}
|
||||
|
||||
public static int getDataVersion() {
|
||||
|
||||
@@ -43,9 +43,8 @@ public class Nebula {
|
||||
@Getter private static PluginManager pluginManager;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Load config + keys first
|
||||
// Load config first
|
||||
Nebula.loadConfig();
|
||||
AeadHelper.loadKeys();
|
||||
|
||||
// Start Server
|
||||
Nebula.getLogger().info("Starting Nebula " + getJarVersion());
|
||||
@@ -54,6 +53,9 @@ public class Nebula {
|
||||
|
||||
boolean generateHandbook = true;
|
||||
|
||||
// Load keys
|
||||
AeadHelper.loadKeys();
|
||||
|
||||
// Load plugin manager
|
||||
Nebula.pluginManager = new PluginManager();
|
||||
|
||||
|
||||
42
src/main/java/emu/nebula/RegionConfig.java
Normal file
42
src/main/java/emu/nebula/RegionConfig.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package emu.nebula;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter @Setter
|
||||
public class RegionConfig {
|
||||
private String name;
|
||||
private int dataVersion;
|
||||
private String serverMetaKey;
|
||||
private String serverGarbleKey;
|
||||
|
||||
private static Object2ObjectMap<String, RegionConfig> REGIONS = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
public RegionConfig(String name) {
|
||||
this.name = name;
|
||||
this.serverMetaKey = "";
|
||||
this.serverGarbleKey = "";
|
||||
}
|
||||
|
||||
public RegionConfig setDataVersion(int i) {
|
||||
this.dataVersion = i;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegionConfig setServerMetaKey(String key) {
|
||||
this.serverMetaKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public RegionConfig setServerGarbleKey(String key) {
|
||||
this.serverGarbleKey = key;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static RegionConfig getRegion(String name) {
|
||||
String regionName = name.toLowerCase();
|
||||
return REGIONS.computeIfAbsent(regionName, r -> new RegionConfig(regionName));
|
||||
}
|
||||
}
|
||||
@@ -7,8 +7,8 @@ import emu.nebula.game.character.GameCharacter;
|
||||
import emu.nebula.game.character.GameDisc;
|
||||
import emu.nebula.game.player.Player;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import lombok.Getter;
|
||||
@@ -53,6 +53,9 @@ public class CommandArgs {
|
||||
} else if (arg.startsWith("lv")) { // Level
|
||||
this.level = Utils.parseSafeInt(arg.substring(2));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("lvl")) { // Level
|
||||
this.level = Utils.parseSafeInt(arg.substring(3));
|
||||
it.remove();
|
||||
} else if (arg.startsWith("a")) { // Advance
|
||||
this.advance = Utils.parseSafeInt(arg.substring(1));
|
||||
it.remove();
|
||||
@@ -76,7 +79,7 @@ public class CommandArgs {
|
||||
int key = Integer.parseInt(split[0]);
|
||||
int value = Integer.parseInt(split[1]);
|
||||
|
||||
if (this.map == null) this.map = new Int2IntOpenHashMap();
|
||||
if (this.map == null) this.map = new Int2IntLinkedOpenHashMap();
|
||||
this.map.put(key, value);
|
||||
|
||||
it.remove();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,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<>();
|
||||
@@ -114,9 +116,12 @@ public class GameData {
|
||||
@Getter private static DataTable<StarTowerGrowthNodeDef> StarTowerGrowthNodeDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerFloorExpDef> StarTowerFloorExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerTeamExpDef> StarTowerTeamExpDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerEventDef> StarTowerEventDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<SubNoteSkillPromoteGroupDef> SubNoteSkillPromoteGroupDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<CharPotentialDef> CharPotentialDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<StarTowerBookFateCardBundleDef> StarTowerBookFateCardBundleDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerBookFateCardQuestDef> StarTowerBookFateCardQuestDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<StarTowerBookFateCardDef> StarTowerBookFateCardDataTable = new DataTable<>();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -21,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();
|
||||
}
|
||||
}
|
||||
@@ -30,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;
|
||||
@@ -71,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;
|
||||
}
|
||||
@@ -79,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;
|
||||
@@ -100,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()) {
|
||||
@@ -118,6 +159,11 @@ public class BattlePass implements GameDatabaseObject {
|
||||
this.syncQuest(quest);
|
||||
}
|
||||
|
||||
// Reset weekly limit for exp
|
||||
if (resetWeekly) {
|
||||
this.expWeek = 0;
|
||||
}
|
||||
|
||||
// Persist to database
|
||||
this.save();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,10 @@ public class BattlePassManager extends PlayerManager {
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean hasNew() {
|
||||
return this.getBattlePass().hasNew();
|
||||
}
|
||||
|
||||
// Database
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -821,14 +821,14 @@ 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());
|
||||
|
||||
@@ -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
@@ -205,7 +205,12 @@ 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().trigger(QuestCondition.TowerEnterFloor, 1);
|
||||
@@ -322,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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package emu.nebula.game.tower;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.PotentialInfo;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerPotentialInfo {
|
||||
private int id;
|
||||
private int level;
|
||||
|
||||
public StarTowerPotentialInfo(int id, int level) {
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public PotentialInfo toProto() {
|
||||
var proto = PotentialInfo.newInstance()
|
||||
.setTid(this.getId())
|
||||
.setLevel(this.getLevel());
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,12 +7,16 @@ import lombok.Getter;
|
||||
@Entity(useDiscriminator = false)
|
||||
public class StarTowerShopGoods {
|
||||
private int type;
|
||||
private int goodsId;
|
||||
private int idx; // This is actually the shop goods id
|
||||
private int goodsId; // Item id
|
||||
private int price;
|
||||
private int discount;
|
||||
private int charPos;
|
||||
private boolean sold;
|
||||
|
||||
public StarTowerShopGoods(int type, int goodsId, int price) {
|
||||
public StarTowerShopGoods(int type, int idx, int goodsId, int price) {
|
||||
this.type = type;
|
||||
this.idx = idx;
|
||||
this.goodsId = goodsId;
|
||||
this.price = price;
|
||||
}
|
||||
@@ -21,4 +25,40 @@ public class StarTowerShopGoods {
|
||||
this.sold = true;
|
||||
}
|
||||
|
||||
public void setCharPos(int charPos) {
|
||||
this.charPos = charPos;
|
||||
}
|
||||
|
||||
public boolean hasDiscount() {
|
||||
return this.getDiscount() > 0;
|
||||
}
|
||||
|
||||
public void applyDiscount(double percentage) {
|
||||
this.discount = (int) Math.ceil(this.price * (1.0 - percentage));
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
return this.price - this.discount;
|
||||
}
|
||||
|
||||
public int getDisplayPrice() {
|
||||
return this.price;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
if (this.getType() == 2) {
|
||||
return this.getIdx() == 8 ? 15 : 5;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int getCharId(StarTowerGame game) {
|
||||
if (this.getCharPos() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int index = this.getCharPos() - 1;
|
||||
return game.getCharIds()[index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package emu.nebula.game.tower;
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -0,0 +1,57 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.StarTowerModifiers;
|
||||
import emu.nebula.game.tower.room.StarTowerBaseRoom;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Base class for star tower cases
|
||||
*/
|
||||
@Getter
|
||||
public abstract class StarTowerBaseCase {
|
||||
private transient StarTowerGame game;
|
||||
private int id;
|
||||
|
||||
public StarTowerBaseCase() {
|
||||
|
||||
}
|
||||
|
||||
public StarTowerBaseRoom getRoom() {
|
||||
return this.getGame().getRoom();
|
||||
}
|
||||
|
||||
public StarTowerModifiers getModifiers() {
|
||||
return this.getGame().getModifiers();
|
||||
}
|
||||
|
||||
public abstract CaseType getType();
|
||||
|
||||
public void register(StarTowerBaseRoom room) {
|
||||
this.game = room.getGame();
|
||||
this.id = room.getNextCaseId();
|
||||
this.onRegister();
|
||||
}
|
||||
|
||||
public void onRegister() {
|
||||
|
||||
}
|
||||
|
||||
public abstract StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp);
|
||||
|
||||
// Proto
|
||||
|
||||
public StarTowerRoomCase toProto() {
|
||||
var proto = StarTowerRoomCase.newInstance()
|
||||
.setId(this.getId());
|
||||
|
||||
this.encodeProto(proto);
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public abstract void encodeProto(StarTowerRoomCase proto);
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerBattleCase extends StarTowerBaseCase {
|
||||
private int subNoteSkillNum;
|
||||
|
||||
public StarTowerBattleCase() {
|
||||
this(0);
|
||||
}
|
||||
|
||||
public StarTowerBattleCase(int subNoteSkillNum) {
|
||||
this.subNoteSkillNum = subNoteSkillNum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.Battle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Parse battle end
|
||||
var proto = req.getBattleEndReq();
|
||||
|
||||
// Init change
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Handle victory/defeat
|
||||
if (proto.hasVictory()) {
|
||||
// Handle leveling up
|
||||
|
||||
// Get relevant floor exp data
|
||||
// fishiatee: THERE'S NO LINQ IN JAVAAAAAAAAAAAAA
|
||||
var floorExpData = GameData.getStarTowerFloorExpDataTable().stream()
|
||||
.filter(f -> f.getStarTowerId() == this.getGame().getId())
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
int expReward = 0;
|
||||
|
||||
// Determine appropriate exp reward
|
||||
switch (this.getRoom().getType()) {
|
||||
// Regular battle room
|
||||
case 0:
|
||||
expReward = floorExpData.getNormalExp();
|
||||
break;
|
||||
// Elite battle room
|
||||
case 1:
|
||||
expReward = floorExpData.getEliteExp();
|
||||
break;
|
||||
// Non-final boss room
|
||||
case 2:
|
||||
expReward = floorExpData.getBossExp();
|
||||
break;
|
||||
// Final room
|
||||
case 3:
|
||||
expReward = floorExpData.getFinalBossExp();
|
||||
break;
|
||||
}
|
||||
|
||||
// Level up
|
||||
this.getGame().addExp(expReward);
|
||||
this.getGame().addPotentialSelectors(this.getGame().levelUp());
|
||||
|
||||
// Add clear time
|
||||
this.getGame().addBattleTime(proto.getVictory().getTime());
|
||||
|
||||
// Handle victory
|
||||
rsp.getMutableBattleEndResp()
|
||||
.getMutableVictory()
|
||||
.setLv(this.getGame().getTeamLevel())
|
||||
.setBattleTime(this.getGame().getBattleTime());
|
||||
|
||||
// Add coin
|
||||
int coin = this.getRoom().getStage().getInteriorCurrencyQuantity();
|
||||
|
||||
this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, coin, change);
|
||||
|
||||
// Handle pending potential selectors
|
||||
var nextCases = this.getGame().handlePendingPotentialSelectors();
|
||||
|
||||
for (var towerCase : nextCases) {
|
||||
this.getGame().addCase(rsp.getMutableCases(), towerCase);
|
||||
}
|
||||
|
||||
// Add sub note skills
|
||||
this.getGame().addRandomSubNoteSkills(this.getGame().getPendingSubNotes(), change);
|
||||
|
||||
// Handle client events for achievements
|
||||
this.getGame().getPlayer().getAchievementManager().handleClientEvents(proto.getVictory().getEvents());
|
||||
} else {
|
||||
// Handle defeat
|
||||
// TODO
|
||||
return this.getGame().settle(rsp, false);
|
||||
}
|
||||
|
||||
// Set change
|
||||
rsp.setChange(change.toProto());
|
||||
|
||||
// Return response for the player
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
proto.getMutableBattleCase()
|
||||
.setSubNoteSkillNum(this.getSubNoteSkillNum());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.data.resources.StarTowerStageDef;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerDoorCase extends StarTowerBaseCase {
|
||||
private int floorNum;
|
||||
private int roomType;
|
||||
|
||||
public StarTowerDoorCase(int floor, StarTowerStageDef data) {
|
||||
this.floorNum = floor;
|
||||
|
||||
if (data != null) {
|
||||
this.roomType = data.getRoomType();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.OpenDoor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Get request
|
||||
var proto = req.getEnterReq();
|
||||
|
||||
// Check if we need to settle on the last floor
|
||||
if (this.getGame().isOnFinalFloor()) {
|
||||
return this.getGame().settle(rsp, true);
|
||||
}
|
||||
|
||||
// Enter next room
|
||||
this.getGame().enterNextRoom();
|
||||
this.getGame().getRoom().setMapInfo(proto);
|
||||
|
||||
// Set room proto
|
||||
rsp.getMutableEnterResp()
|
||||
.setRoom(this.getRoom().toProto());
|
||||
|
||||
// Done
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
proto.getMutableDoorCase()
|
||||
.setFloor(this.getFloorNum())
|
||||
.setType(this.getRoomType());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.game.tower.StarTowerShopGoods;
|
||||
import emu.nebula.proto.PublicStarTower.HawkerCaseData;
|
||||
import emu.nebula.proto.PublicStarTower.HawkerGoods;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerHawkerCase extends StarTowerBaseCase {
|
||||
private Map<Integer, StarTowerShopGoods> goods;
|
||||
|
||||
public StarTowerHawkerCase() {
|
||||
this.goods = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.Hawker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegister() {
|
||||
this.initGoods();
|
||||
}
|
||||
|
||||
public void initGoods() {
|
||||
// Clear goods
|
||||
this.getGoods().clear();
|
||||
|
||||
// Caclulate amount of potentials/sub notes to sell
|
||||
int total = getModifiers().getShopGoodsCount();
|
||||
|
||||
int minPotentials = Math.max(total / 2, 2);
|
||||
int maxPotentials = Math.max(total - 1, minPotentials);
|
||||
int potentials = Utils.randomRange(minPotentials, maxPotentials);
|
||||
|
||||
int subNotes = total - potentials;
|
||||
boolean hasCoins = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID) >= 500;
|
||||
|
||||
// Add goods
|
||||
for (int i = 0; i < potentials; i++) {
|
||||
// Create potential selector shop item
|
||||
var goods = new StarTowerShopGoods(1, 1, 102, 200);
|
||||
|
||||
// Add character specific potentials
|
||||
if (Utils.generateRandomDouble() < .2) {
|
||||
goods.setCharPos(1);
|
||||
}
|
||||
|
||||
// Add to goods map
|
||||
this.addGoods(goods);
|
||||
}
|
||||
for (int i = 0; i < subNotes; i++) {
|
||||
// Randomize sub note
|
||||
int id = Utils.randomElement(this.getGame().getSubNoteDropList());
|
||||
|
||||
// Create sub note shop item
|
||||
StarTowerShopGoods goods = null;
|
||||
|
||||
if (hasCoins && Utils.randomChance(.25)) {
|
||||
goods = new StarTowerShopGoods(2, 8, id, 400);
|
||||
} else {
|
||||
goods = new StarTowerShopGoods(2, 3, id, 90);
|
||||
}
|
||||
|
||||
// Add to goods map
|
||||
this.addGoods(goods);
|
||||
}
|
||||
|
||||
// Apply discounts based on star tower growth nodes
|
||||
if (getModifiers().isShopDiscountTier1()) {
|
||||
this.applyDiscount(1.0, 2, 0.8);
|
||||
}
|
||||
if (getModifiers().isShopDiscountTier2()) {
|
||||
this.applyDiscount(0.3, 1, 0.5);
|
||||
}
|
||||
if (getModifiers().isShopDiscountTier3()) {
|
||||
this.applyDiscount(1.0, 1, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDiscount(double chance, int times, double percentage) {
|
||||
// Check chance
|
||||
double random = Utils.generateRandomDouble();
|
||||
|
||||
if (random > chance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create goods list
|
||||
var list = this.getGoods().values().stream()
|
||||
.filter(g -> !g.hasDiscount())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Apply discounts
|
||||
for (int i = 0; i < times; i++) {
|
||||
// Sanity check
|
||||
if (list.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Get goods and apply discount
|
||||
var goods = Utils.randomElement(list, true);
|
||||
goods.applyDiscount(percentage);
|
||||
}
|
||||
}
|
||||
|
||||
public void addGoods(StarTowerShopGoods goods) {
|
||||
this.getGoods().put(getGoods().size() + 1, goods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Set nil resp
|
||||
rsp.getMutableNilResp();
|
||||
|
||||
// Get hawker req
|
||||
var hawker = req.getHawkerReq();
|
||||
|
||||
if (hawker.hasReRoll()) {
|
||||
// Refresh shop items
|
||||
this.refresh(rsp);
|
||||
} else if (hawker.hasSid()) {
|
||||
// Buy shop items
|
||||
this.buy(hawker.getSid(), rsp);
|
||||
}
|
||||
|
||||
// Success
|
||||
return rsp;
|
||||
}
|
||||
|
||||
private void refresh(StarTowerInteractResp rsp) {
|
||||
// Check if we can refresh
|
||||
if (this.getModifiers().getShopRerollCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have enough currency
|
||||
int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID);
|
||||
int price = this.getModifiers().getShopRerollPrice();
|
||||
|
||||
if (coin < price) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new goods
|
||||
this.initGoods();
|
||||
|
||||
// Consume reroll count
|
||||
this.getGame().getModifiers().consumeShopReroll();
|
||||
|
||||
// Set in proto
|
||||
rsp.getMutableSelectResp()
|
||||
.setHawkerCase(this.toHawkerCaseProto());
|
||||
|
||||
// Remove coins
|
||||
var change = this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price);
|
||||
|
||||
// Set change info
|
||||
rsp.setChange(change.toProto());
|
||||
}
|
||||
|
||||
private void buy(int sid, StarTowerInteractResp rsp) {
|
||||
// Get goods
|
||||
var goods = this.getGoods().get(sid);
|
||||
if (goods == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have enough currency
|
||||
int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID);
|
||||
int price = goods.getPrice();
|
||||
|
||||
if (coin < price || goods.isSold()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark goods as sold
|
||||
goods.markAsSold();
|
||||
|
||||
// Create change info
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Add goods
|
||||
if (goods.getType() == 1) {
|
||||
// Potential selector
|
||||
int charId = goods.getCharId(this.getGame());
|
||||
this.getGame().addCase(rsp.getMutableCases(), this.getGame().createPotentialSelector(charId));
|
||||
} else {
|
||||
// Sub notes
|
||||
this.getGame().addItem(goods.getGoodsId(), goods.getCount(), change);
|
||||
}
|
||||
|
||||
// Remove coins
|
||||
this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price, change);
|
||||
|
||||
// Set change info
|
||||
rsp.setChange(change.toProto());
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
private HawkerCaseData toHawkerCaseProto() {
|
||||
var hawker = HawkerCaseData.newInstance();
|
||||
|
||||
if (this.getModifiers().getShopRerollCount() > 0) {
|
||||
hawker.setCanReRoll(true);
|
||||
hawker.setReRollTimes(this.getModifiers().getShopRerollCount());
|
||||
hawker.setReRollPrice(this.getModifiers().getShopRerollPrice());
|
||||
}
|
||||
|
||||
for (var entry : this.getGoods().entrySet()) {
|
||||
var sid = entry.getKey();
|
||||
var goods = entry.getValue();
|
||||
|
||||
var info = HawkerGoods.newInstance()
|
||||
.setSid(sid)
|
||||
.setType(goods.getType())
|
||||
.setIdx(goods.getIdx())
|
||||
.setGoodsId(goods.getGoodsId())
|
||||
.setPrice(goods.getDisplayPrice())
|
||||
.setTag(1);
|
||||
|
||||
if (goods.hasDiscount()) {
|
||||
info.setDiscount(goods.getPrice());
|
||||
}
|
||||
|
||||
if (goods.getCharPos() > 0) {
|
||||
info.setCharPos(goods.getCharPos());
|
||||
}
|
||||
|
||||
hawker.addList(info);
|
||||
}
|
||||
|
||||
return hawker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
proto.setHawkerCase(this.toHawkerCaseProto());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.resources.StarTowerEventDef;
|
||||
import emu.nebula.game.player.PlayerChangeInfo;
|
||||
import emu.nebula.proto.PublicStarTower.NPCAffinityInfo;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import emu.nebula.util.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerNpcEventCase extends StarTowerBaseCase {
|
||||
private int npcId;
|
||||
private int eventId;
|
||||
private IntList options;
|
||||
private boolean completed;
|
||||
|
||||
public StarTowerNpcEventCase(int npcId, StarTowerEventDef event) {
|
||||
this.npcId = npcId;
|
||||
this.eventId = event.getId();
|
||||
this.options = new IntArrayList();
|
||||
|
||||
// Add up to 4 random options
|
||||
var randomOptions = event.getClonedOptionIds();
|
||||
int maxOptions = Math.min(randomOptions.size(), 4);
|
||||
|
||||
for (int i = 0; i < maxOptions; i++) {
|
||||
int optionId = Utils.randomElement(randomOptions, true);
|
||||
this.options.add(optionId);
|
||||
}
|
||||
|
||||
// Fix for question type events to always include the answer
|
||||
if (this.eventId >= 114 && this.eventId <= 116) {
|
||||
int answerId = (this.eventId * 100) + 3;
|
||||
if (!this.getOptions().contains(answerId)) {
|
||||
this.getOptions().set(0, answerId);
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle
|
||||
Collections.shuffle(this.getOptions());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.NpcEvent;
|
||||
}
|
||||
|
||||
public int getOption(int index) {
|
||||
if (index < 0 || index >= this.getOptions().size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return this.getOptions().getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Sanity check to make sure we cant do the event multiple times
|
||||
if (this.isCompleted()) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Get option from selection index
|
||||
int option = this.getOption(req.getSelectReq().getIndex());
|
||||
|
||||
// Get select response proto
|
||||
var selectRsp = rsp.getMutableSelectResp();
|
||||
var success = selectRsp.getMutableResp();
|
||||
var change = new PlayerChangeInfo();
|
||||
|
||||
// Completed event flag
|
||||
boolean completed = true;
|
||||
|
||||
// Handle option id
|
||||
switch (option) {
|
||||
case 10101 -> {
|
||||
if (this.spendCoin(100, change)) {
|
||||
this.addPotentialSelector(rsp);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10102 -> {
|
||||
if (this.spendCoin(120, change)) {
|
||||
this.addPotentialSelector(rsp);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10103 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10201 -> {
|
||||
if (this.spendCoin(120, change)) {
|
||||
this.addPotentialSelector(rsp, this.getRandomSupportCharId());
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10202 -> {
|
||||
if (this.spendCoin(160, change)) {
|
||||
this.addPotentialSelector(rsp, this.getMainCharId());
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10203 -> {
|
||||
if (this.spendCoin(200, change)) {
|
||||
this.addRarePotentialSelector(rsp);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10204 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10302 -> {
|
||||
// TODO
|
||||
if (this.spendSubNotes(5, change)) {
|
||||
this.addCoin(150, change);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10303 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10401 -> {
|
||||
// TODO
|
||||
completed = false;
|
||||
}
|
||||
case 10402 -> {
|
||||
if (this.spendCoin(200, change)) {
|
||||
this.addRarePotentialSelector(rsp);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10403 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10501 -> {
|
||||
if (Utils.randomChance(.5)) {
|
||||
this.addCoin(200, change);
|
||||
} else {
|
||||
this.addCoin(-100, change);
|
||||
}
|
||||
}
|
||||
case 10502 -> {
|
||||
if (Utils.randomChance(.3)) {
|
||||
this.addCoin(650, change);
|
||||
} else {
|
||||
this.addCoin(-200, change);
|
||||
}
|
||||
}
|
||||
case 10503 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10601 -> {
|
||||
if (Utils.randomChance(.5)) {
|
||||
this.addRarePotentialSelector(rsp);
|
||||
}
|
||||
}
|
||||
case 10602 -> {
|
||||
this.addPotentialSelector(rsp);
|
||||
}
|
||||
case 10603 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 10701, 10702, 10703, 10704, 10705, 10706, 10707 -> {
|
||||
int subNoteId = (option % 100) + 90010;
|
||||
this.getGame().addItem(subNoteId, 5, change);
|
||||
}
|
||||
case 10708 -> {
|
||||
int subNoteId = this.getGame().getRandomSubNoteId();
|
||||
this.getGame().addItem(subNoteId, 5, change);
|
||||
}
|
||||
case 10801, 10802, 10803, 10804, 10805, 10806, 10807 -> {
|
||||
if (this.spendCoin(140, change)) {
|
||||
int subNoteId = (option % 100) + 90010;
|
||||
this.getGame().addItem(subNoteId, 10, change);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10808 -> {
|
||||
if (this.spendCoin(90, change)) {
|
||||
int subNoteId = this.getGame().getRandomSubNoteId();
|
||||
this.getGame().addItem(subNoteId, 10, change);
|
||||
} else {
|
||||
completed = false;
|
||||
}
|
||||
}
|
||||
case 10809 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
case 11401, 11402, 11403, 11404, 11405 -> {
|
||||
if (option == 11403) {
|
||||
int subNoteId = this.getGame().getRandomSubNoteId();
|
||||
this.getGame().addItem(subNoteId, 10, change);
|
||||
} else {
|
||||
success.setOptionsParamId(100140101);
|
||||
}
|
||||
}
|
||||
case 11501, 11502, 11503, 11504, 11505 -> {
|
||||
if (option == 11503) {
|
||||
this.addPotentialSelector(rsp);
|
||||
} else {
|
||||
success.setOptionsParamId(100140101);
|
||||
}
|
||||
}
|
||||
case 11601, 11602, 11603, 11604, 11605 -> {
|
||||
if (option == 11603) {
|
||||
this.addRarePotentialSelector(rsp);
|
||||
} else {
|
||||
success.setOptionsParamId(100140101);
|
||||
}
|
||||
}
|
||||
case 12601 -> {
|
||||
this.addPotentialSelector(rsp, this.getRandomSupportCharId());
|
||||
}
|
||||
case 12602 -> {
|
||||
// Recover 20% hp
|
||||
}
|
||||
case 12701 -> {
|
||||
this.addPotentialSelector(rsp, this.getRandomSupportCharId());
|
||||
}
|
||||
case 12702 -> {
|
||||
int subNoteId = this.getGame().getRandomSubNoteId();
|
||||
this.getGame().addItem(subNoteId, 5, change);
|
||||
}
|
||||
case 12801 -> {
|
||||
this.addRarePotentialSelector(rsp, this.getRandomSupportCharId());
|
||||
}
|
||||
case 12802 -> {
|
||||
this.addCoin(30, change);
|
||||
}
|
||||
default -> {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
// Set change info
|
||||
rsp.setChange(change.toProto());
|
||||
|
||||
// Set success result
|
||||
success.setOptionsResult(completed);
|
||||
this.completed = completed;
|
||||
|
||||
// Complete
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
private boolean spendCoin(int amount, PlayerChangeInfo change) {
|
||||
int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID);
|
||||
|
||||
if (coin < amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.addCoin(-amount, change);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private PlayerChangeInfo addCoin(int amount, PlayerChangeInfo change) {
|
||||
return this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, amount, change);
|
||||
}
|
||||
|
||||
private boolean spendSubNotes(int amount, PlayerChangeInfo change) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addPotentialSelector(StarTowerInteractResp rsp) {
|
||||
this.addPotentialSelector(rsp, 0);
|
||||
}
|
||||
|
||||
private void addPotentialSelector(StarTowerInteractResp rsp, int charId) {
|
||||
var selectorCase = this.getGame().createPotentialSelector(charId);
|
||||
this.getRoom().addCase(rsp.getMutableCases(), selectorCase);
|
||||
}
|
||||
|
||||
private void addRarePotentialSelector(StarTowerInteractResp rsp) {
|
||||
this.addRarePotentialSelector(rsp, 0);
|
||||
}
|
||||
|
||||
private void addRarePotentialSelector(StarTowerInteractResp rsp, int charId) {
|
||||
var selectorCase = this.getGame().createPotentialSelector(charId, true);
|
||||
this.getRoom().addCase(rsp.getMutableCases(), selectorCase);
|
||||
}
|
||||
|
||||
private int getMainCharId() {
|
||||
return this.getGame().getCharIds()[0];
|
||||
}
|
||||
|
||||
private int getRandomSupportCharId() {
|
||||
return this.getGame().getCharIds()[Utils.randomRange(1, 2)];
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
var info = NPCAffinityInfo.newInstance()
|
||||
.setNPCId(this.getNpcId())
|
||||
.setAffinity(0);
|
||||
|
||||
proto.getMutableSelectOptionsEventCase()
|
||||
.setEvtId(this.getEventId())
|
||||
.setNPCId(this.getNpcId())
|
||||
.addInfos(info)
|
||||
.addAllOptions(this.getOptions().toIntArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerNpcRecoveryHPCase extends StarTowerBaseCase {
|
||||
private int effectId;
|
||||
|
||||
public StarTowerNpcRecoveryHPCase() {
|
||||
this(989970); // Restore Hp/Energy by 50%
|
||||
}
|
||||
|
||||
public StarTowerNpcRecoveryHPCase(int effectId) {
|
||||
this.effectId = effectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.NpcRecoveryHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
// Set case info
|
||||
proto.getMutableNpcRecoveryHPCase()
|
||||
.setEffectId(this.getEffectId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.StarTowerPotentialInfo;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerPotentialCase extends StarTowerBaseCase {
|
||||
private int teamLevel;
|
||||
private int charId;
|
||||
private int reroll;
|
||||
private int rerollPrice;
|
||||
private boolean strengthen;
|
||||
private List<StarTowerPotentialInfo> potentials;
|
||||
|
||||
public StarTowerPotentialCase(StarTowerGame game, boolean strengthen, List<StarTowerPotentialInfo> potentials) {
|
||||
this(game, 0, potentials);
|
||||
this.strengthen = strengthen;
|
||||
}
|
||||
|
||||
public StarTowerPotentialCase(StarTowerGame game, int charId, List<StarTowerPotentialInfo> potentials) {
|
||||
this.teamLevel = game.getTeamLevel();
|
||||
this.charId = charId;
|
||||
this.reroll = game.getModifiers().getPotentialRerollCount();
|
||||
this.rerollPrice = 100 - game.getModifiers().getPotentialRerollDiscount();
|
||||
this.potentials = potentials;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.PotentialSelect;
|
||||
}
|
||||
|
||||
public boolean isRare() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setReroll(int count) {
|
||||
this.reroll = count;
|
||||
}
|
||||
|
||||
public boolean canReroll() {
|
||||
return this.reroll > 0;
|
||||
}
|
||||
|
||||
public StarTowerPotentialInfo selectId(int index) {
|
||||
if (index < 0 || index >= this.getPotentials().size()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.getPotentials().get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Check
|
||||
var select = req.getMutableSelectReq();
|
||||
|
||||
if (select.hasReRoll()) {
|
||||
return this.reroll(rsp);
|
||||
} else {
|
||||
return this.select(select.getIndex(), rsp);
|
||||
}
|
||||
}
|
||||
|
||||
private StarTowerInteractResp reroll(StarTowerInteractResp rsp) {
|
||||
// Check if we can reroll
|
||||
if (!this.canReroll()) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Check price
|
||||
int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID);
|
||||
int price = this.getRerollPrice();
|
||||
|
||||
if (coin < price) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Subtract rerolls
|
||||
int newReroll = this.reroll - 1;
|
||||
|
||||
// Create reroll case
|
||||
StarTowerPotentialCase rerollCase = null;
|
||||
|
||||
if (this.isStrengthen()) {
|
||||
rerollCase = this.getGame().createStrengthenSelector();
|
||||
} else {
|
||||
rerollCase = this.getGame().createPotentialSelector(this.getCharId(), this.isRare());
|
||||
}
|
||||
|
||||
if (rerollCase == null) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Clear reroll count
|
||||
rerollCase.setReroll(newReroll);
|
||||
|
||||
// Add reroll case
|
||||
this.getRoom().addCase(rsp.getMutableCases(), rerollCase);
|
||||
|
||||
// Finish subtracting rerolls
|
||||
this.reroll = newReroll;
|
||||
|
||||
// Subtract coins
|
||||
var change = this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price);
|
||||
|
||||
rsp.setChange(change.toProto());
|
||||
|
||||
// Complete
|
||||
return rsp;
|
||||
}
|
||||
|
||||
private StarTowerInteractResp select(int index, StarTowerInteractResp rsp) {
|
||||
// Get selected potential
|
||||
var potential = this.selectId(index);
|
||||
if (potential == null) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Add potential
|
||||
var change = this.getGame().addItem(potential.getId(), potential.getLevel());
|
||||
|
||||
// Set change
|
||||
rsp.setChange(change.toProto());
|
||||
|
||||
// Handle pending potential selectors
|
||||
var nextCases = this.getGame().handlePendingPotentialSelectors();
|
||||
|
||||
for (var towerCase : nextCases) {
|
||||
this.getRoom().addCase(rsp.getMutableCases(), towerCase);
|
||||
}
|
||||
|
||||
// Complete
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
var select = proto.getMutableSelectPotentialCase()
|
||||
.setTeamLevel(this.getTeamLevel());
|
||||
|
||||
for (var potential : this.getPotentials()) {
|
||||
select.addInfos(potential.toProto());
|
||||
}
|
||||
|
||||
if (this.canReroll()) {
|
||||
select.setCanReRoll(true);
|
||||
select.setReRollPrice(this.getRerollPrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerRecoveryHPCase extends StarTowerBaseCase {
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.RecoveryHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Set nil resp
|
||||
rsp.getMutableNilResp();
|
||||
|
||||
// Add sync hp case
|
||||
this.getGame().addCase(rsp.getMutableCases(), new StarTowerSyncHPCase());
|
||||
|
||||
// Return
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
// Set field in the proto
|
||||
proto.getMutableRecoveryHPCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.StarTowerPotentialInfo;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerSelectSpecialPotentialCase extends StarTowerPotentialCase {
|
||||
|
||||
public StarTowerSelectSpecialPotentialCase(StarTowerGame game, int charId, List<StarTowerPotentialInfo> potentials) {
|
||||
super(game, charId, potentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.SelectSpecialPotential;
|
||||
}
|
||||
|
||||
public boolean isRare() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
var select = proto.getMutableSelectSpecialPotentialCase()
|
||||
.setTeamLevel(this.getTeamLevel());
|
||||
|
||||
for (var potential : this.getPotentials()) {
|
||||
select.addIds(potential.getId());
|
||||
}
|
||||
|
||||
if (this.canReroll()) {
|
||||
select.setCanReRoll(true);
|
||||
select.setReRollPrice(this.getRerollPrice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerStrengthenMachineCase extends StarTowerBaseCase {
|
||||
private boolean free;
|
||||
private int discount;
|
||||
private int times;
|
||||
|
||||
@Override
|
||||
public void onRegister() {
|
||||
// Set strengthen price
|
||||
this.free = this.getModifiers().isFreeStrengthen();
|
||||
this.discount = this.getModifiers().getStrengthenDiscount();
|
||||
}
|
||||
|
||||
public int getPrice() {
|
||||
if (this.free) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int price = 120 + (this.times * 60) - this.discount;
|
||||
|
||||
return Math.max(price, 0);
|
||||
}
|
||||
|
||||
public void increasePrice() {
|
||||
if (this.free) {
|
||||
this.free = false;
|
||||
this.getModifiers().setFreeStrengthen(false);
|
||||
} else {
|
||||
this.times++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.StrengthenMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
// Init case
|
||||
StarTowerBaseCase towerCase = null;
|
||||
|
||||
// Check coin
|
||||
int coin = getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID);
|
||||
int price = this.getPrice();
|
||||
|
||||
if (coin >= price) {
|
||||
towerCase = getGame().createStrengthenSelector();
|
||||
}
|
||||
|
||||
if (towerCase != null) {
|
||||
// Add enhancement selector case
|
||||
this.getRoom().addCase(rsp.getMutableCases(), towerCase);
|
||||
|
||||
// Remove coins
|
||||
var change = this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price);
|
||||
|
||||
// Set change info
|
||||
rsp.setChange(change.toProto());
|
||||
|
||||
// Increment price
|
||||
this.increasePrice();
|
||||
}
|
||||
|
||||
// Set success result
|
||||
rsp.getMutableStrengthenMachineResp()
|
||||
.setBuySucceed(towerCase != null);
|
||||
|
||||
// Complete
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
// Set field in the proto
|
||||
proto.getMutableStrengthenMachineCase()
|
||||
.setFirstFree(this.isFree())
|
||||
.setDiscount(this.getDiscount())
|
||||
.setTimes(this.getTimes());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package emu.nebula.game.tower.cases;
|
||||
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
|
||||
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerSyncHPCase extends StarTowerBaseCase {
|
||||
|
||||
@Override
|
||||
public CaseType getType() {
|
||||
return CaseType.SyncHP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StarTowerInteractResp interact(StarTowerInteractReq req, StarTowerInteractResp rsp) {
|
||||
return rsp;
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
@Override
|
||||
public void encodeProto(StarTowerRoomCase proto) {
|
||||
// Set field in the proto
|
||||
proto.getMutableSyncHPCase();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package emu.nebula.game.tower;
|
||||
package emu.nebula.game.tower.room;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
||||
public enum StarTowerRoomType {
|
||||
public enum RoomType {
|
||||
BattleRoom (0),
|
||||
EliteBattleRoom (1),
|
||||
BossRoom (2),
|
||||
@@ -17,19 +17,19 @@ public enum StarTowerRoomType {
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
private final static Int2ObjectMap<StarTowerRoomType> map = new Int2ObjectOpenHashMap<>();
|
||||
private final static Int2ObjectMap<RoomType> map = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
for (StarTowerRoomType type : StarTowerRoomType.values()) {
|
||||
for (RoomType type : RoomType.values()) {
|
||||
map.put(type.getValue(), type);
|
||||
}
|
||||
}
|
||||
|
||||
private StarTowerRoomType(int value) {
|
||||
private RoomType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static StarTowerRoomType getByValue(int value) {
|
||||
public static RoomType getByValue(int value) {
|
||||
return map.get(value);
|
||||
}
|
||||
}
|
||||
158
src/main/java/emu/nebula/game/tower/room/StarTowerBaseRoom.java
Normal file
158
src/main/java/emu/nebula/game/tower/room/StarTowerBaseRoom.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package emu.nebula.game.tower.room;
|
||||
|
||||
import emu.nebula.data.resources.StarTowerStageDef;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.StarTowerModifiers;
|
||||
import emu.nebula.game.tower.cases.CaseType;
|
||||
import emu.nebula.game.tower.cases.StarTowerBaseCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerSyncHPCase;
|
||||
import emu.nebula.proto.PublicStarTower.InteractEnterReq;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
|
||||
import emu.nebula.proto.PublicStarTower.StarTowerRoomData;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import us.hebi.quickbuf.RepeatedMessage;
|
||||
|
||||
@Getter
|
||||
public class StarTowerBaseRoom {
|
||||
// Game
|
||||
private transient StarTowerGame game;
|
||||
private transient StarTowerStageDef stage;
|
||||
|
||||
// Map info
|
||||
private int mapId;
|
||||
private int mapTableId;
|
||||
private String mapParam;
|
||||
private int paramId;
|
||||
|
||||
// Cases
|
||||
private int lastCaseId = 0;
|
||||
private Int2ObjectMap<StarTowerBaseCase> cases;
|
||||
|
||||
// Misc
|
||||
private boolean hasDoor;
|
||||
|
||||
public StarTowerBaseRoom(StarTowerGame game, StarTowerStageDef stage) {
|
||||
this.game = game;
|
||||
this.stage = stage;
|
||||
this.cases = new Int2ObjectLinkedOpenHashMap<>();
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return stage.getRoomType();
|
||||
}
|
||||
|
||||
public boolean hasDoor() {
|
||||
return this.hasDoor;
|
||||
}
|
||||
|
||||
public StarTowerModifiers getModifiers() {
|
||||
return this.getGame().getModifiers();
|
||||
}
|
||||
|
||||
public StarTowerBaseCase createExit() {
|
||||
return this.getGame().createExit();
|
||||
}
|
||||
|
||||
// Map info
|
||||
|
||||
public void setMapInfo(StarTowerApplyReq req) {
|
||||
this.mapId = req.getMapId();
|
||||
this.mapTableId = req.getMapTableId();
|
||||
this.mapParam = req.getMapParam();
|
||||
this.paramId = req.getParamId();
|
||||
}
|
||||
|
||||
public void setMapInfo(InteractEnterReq req) {
|
||||
this.mapId = req.getMapId();
|
||||
this.mapTableId = req.getMapTableId();
|
||||
this.mapParam = req.getMapParam();
|
||||
this.paramId = req.getParamId();
|
||||
}
|
||||
|
||||
// Cases
|
||||
|
||||
public int getNextCaseId() {
|
||||
return ++this.lastCaseId;
|
||||
}
|
||||
|
||||
public StarTowerBaseCase getCase(int id) {
|
||||
return this.getCases().get(id);
|
||||
}
|
||||
|
||||
public StarTowerBaseCase addCase(StarTowerBaseCase towerCase) {
|
||||
return this.addCase(null, towerCase);
|
||||
}
|
||||
|
||||
public StarTowerBaseCase addCase(RepeatedMessage<StarTowerRoomCase> cases, StarTowerBaseCase towerCase) {
|
||||
// Sanity check
|
||||
if (towerCase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set game for tower case
|
||||
towerCase.register(this);
|
||||
|
||||
// Add to cases list
|
||||
this.getCases().put(towerCase.getId(), towerCase);
|
||||
|
||||
// Add case to proto
|
||||
if (cases != null) {
|
||||
cases.add(towerCase.toProto());
|
||||
}
|
||||
|
||||
// Check if door case
|
||||
if (towerCase.getType() == CaseType.OpenDoor) {
|
||||
this.hasDoor = true;
|
||||
}
|
||||
|
||||
// Complete
|
||||
return towerCase;
|
||||
}
|
||||
|
||||
// Events
|
||||
|
||||
public void onEnter() {
|
||||
// Create sync hp case
|
||||
this.addCase(new StarTowerSyncHPCase());
|
||||
|
||||
// Create door case
|
||||
this.createExit();
|
||||
}
|
||||
|
||||
// Proto
|
||||
|
||||
public emu.nebula.proto.PublicStarTower.StarTowerRoom toProto() {
|
||||
var proto = emu.nebula.proto.PublicStarTower.StarTowerRoom.newInstance()
|
||||
.setData(this.getDataProto());
|
||||
|
||||
for (var towerCase : this.getCases().values()) {
|
||||
proto.addCases(towerCase.toProto());
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
private StarTowerRoomData getDataProto() {
|
||||
var proto = StarTowerRoomData.newInstance()
|
||||
.setFloor(this.getGame().getFloorCount())
|
||||
.setMapId(this.getMapId())
|
||||
.setRoomType(this.getType())
|
||||
.setMapTableId(this.getMapTableId());
|
||||
|
||||
if (this.getMapParam() != null && !this.getMapParam().isEmpty()) {
|
||||
proto.setMapParam(this.getMapParam());
|
||||
}
|
||||
|
||||
if (this.getParamId() != 0) {
|
||||
proto.setParamId(this.getParamId());
|
||||
}
|
||||
|
||||
return proto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package emu.nebula.game.tower.room;
|
||||
|
||||
import emu.nebula.data.resources.StarTowerStageDef;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.cases.StarTowerBattleCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerSyncHPCase;
|
||||
import emu.nebula.util.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerBattleRoom extends StarTowerBaseRoom {
|
||||
|
||||
public StarTowerBattleRoom(StarTowerGame game, StarTowerStageDef stage) {
|
||||
super(game, stage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnter() {
|
||||
// Create battle case
|
||||
this.getGame().setPendingSubNotes(Utils.randomRange(1, 3));
|
||||
this.addCase(new StarTowerBattleCase(this.getGame().getPendingSubNotes()));
|
||||
|
||||
// Create sync hp case
|
||||
this.addCase(new StarTowerSyncHPCase());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package emu.nebula.game.tower.room;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.resources.StarTowerEventDef;
|
||||
import emu.nebula.data.resources.StarTowerStageDef;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.cases.StarTowerBaseCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerNpcEventCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerSyncHPCase;
|
||||
import emu.nebula.util.Utils;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerEventRoom extends StarTowerBaseRoom {
|
||||
|
||||
public StarTowerEventRoom(StarTowerGame game, StarTowerStageDef stage) {
|
||||
super(game, stage);
|
||||
}
|
||||
|
||||
private StarTowerEventDef getRandomEvent() {
|
||||
/*
|
||||
var list = GameData.getStarTowerEventDataTable()
|
||||
.values()
|
||||
.stream()
|
||||
.toList();
|
||||
*/
|
||||
|
||||
var list = Arrays.stream(GameConstants.TOWER_EVENTS_IDS)
|
||||
.mapToObj(GameData.getStarTowerEventDataTable()::get)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.randomElement(list);
|
||||
}
|
||||
|
||||
public StarTowerBaseCase createNpcEvent() {
|
||||
// Get random event
|
||||
var event = this.getRandomEvent();
|
||||
|
||||
if (event == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get random npc
|
||||
int npcId = Utils.randomElement(event.getRelatedNPCs());
|
||||
|
||||
// Create case with event
|
||||
return new StarTowerNpcEventCase(npcId, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnter() {
|
||||
// Create npc
|
||||
this.addCase(this.createNpcEvent());
|
||||
|
||||
// Create sync hp case
|
||||
this.addCase(new StarTowerSyncHPCase());
|
||||
|
||||
// Create door case
|
||||
this.createExit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package emu.nebula.game.tower.room;
|
||||
|
||||
import emu.nebula.data.resources.StarTowerStageDef;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.game.tower.cases.StarTowerHawkerCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerStrengthenMachineCase;
|
||||
import emu.nebula.game.tower.cases.StarTowerSyncHPCase;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class StarTowerHawkerRoom extends StarTowerBaseRoom {
|
||||
|
||||
public StarTowerHawkerRoom(StarTowerGame game, StarTowerStageDef stage) {
|
||||
super(game, stage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnter() {
|
||||
// Create hawker case (shop)
|
||||
this.addCase(new StarTowerHawkerCase());
|
||||
|
||||
// Create strengthen machine
|
||||
if (this.getModifiers().isEnableShopStrengthen()) {
|
||||
this.addCase(new StarTowerStrengthenMachineCase());
|
||||
}
|
||||
|
||||
// Create sync hp case
|
||||
this.addCase(new StarTowerSyncHPCase());
|
||||
|
||||
// Create door case
|
||||
this.createExit();
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,9 @@ public class HttpServer {
|
||||
if (this.getType().runGame()) {
|
||||
this.addGameServerRoutes();
|
||||
}
|
||||
|
||||
// Custom api route(s)
|
||||
getApp().post("/api/command", new RemoteHandler());
|
||||
|
||||
// Exception handler
|
||||
getApp().exception(Exception.class, (e, c) -> {
|
||||
@@ -154,8 +157,8 @@ public class HttpServer {
|
||||
private void addLoginServerRoutes() {
|
||||
// https://en-sdk-api.yostarplat.com/
|
||||
getApp().post("/common/config", new CommonConfigHandler(this));
|
||||
getApp().post("/common/version", new HttpJsonResponse(
|
||||
"{\"Code\":200,\"Data\":{\"Agreement\":[{\"Version\":\"0.1\",\"Type\":\"user_agreement\",\"Title\":\"用户协议\",\"Content\":\"\",\"Lang\":\"en\"},{\"Version\":\"0.1\",\"Type\":\"privacy_agreement\",\"Title\":\"隐私政策\",\"Content\":\"\",\"Lang\":\"en\"}],\"ErrorCode\":\"4.4\"},\"Msg\":\"OK\"}"));
|
||||
getApp().post("/common/client-code", new CommonClientCodeHandler());
|
||||
getApp().post("/common/version", new HttpJsonResponse("{\"Code\":200,\"Data\":{\"Agreement\":[{\"Version\":\"0.1\",\"Type\":\"user_agreement\",\"Title\":\"用户协议\",\"Content\":\"\",\"Lang\":\"en\"},{\"Version\":\"0.1\",\"Type\":\"privacy_agreement\",\"Title\":\"隐私政策\",\"Content\":\"\",\"Lang\":\"en\"}],\"ErrorCode\":\"4.4\"},\"Msg\":\"OK\"}"));
|
||||
|
||||
getApp().post("/user/detail", new UserLoginHandler());
|
||||
getApp().post("/user/set", new UserSetDataHandler());
|
||||
@@ -163,16 +166,11 @@ public class HttpServer {
|
||||
getApp().post("/user/quick-login", new UserLoginHandler());
|
||||
|
||||
getApp().post("/yostar/get-auth", new GetAuthHandler());
|
||||
getApp().post("/yostar/send-code", new HttpJsonResponse("{\"Code\":200,\"Data\":{},\"Msg\":\"OK\"}")); // Dummy
|
||||
// handler
|
||||
getApp().post("/yostar/send-code", new HttpJsonResponse("{\"Code\":200,\"Data\":{},\"Msg\":\"OK\"}")); // Dummy handler
|
||||
|
||||
// https://nova-static.stellasora.global/
|
||||
getApp().get("/meta/serverlist.html", new MetaServerlistHandler(this));
|
||||
getApp().get("/meta/win.html", new MetaWinHandler(this));
|
||||
|
||||
getApp().post("/api/command", new RemoteHandler());
|
||||
|
||||
|
||||
getApp().get("/meta/*.html", new MetaPatchListHandler(this));
|
||||
}
|
||||
|
||||
private void addGameServerRoutes() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import emu.nebula.net.NetMsgId;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
|
||||
import emu.nebula.proto.StarTowerApply.StarTowerApplyResp;
|
||||
import emu.nebula.net.HandlerId;
|
||||
import emu.nebula.GameConstants;
|
||||
import emu.nebula.game.tower.StarTowerGame;
|
||||
import emu.nebula.net.GameSession;
|
||||
|
||||
@@ -29,6 +30,7 @@ public class HandlerStarTowerApplyReq extends NetHandler {
|
||||
// Create response
|
||||
var rsp = StarTowerApplyResp.newInstance()
|
||||
.setLastId(req.getId())
|
||||
.setCoinQty(game.getResCount(GameConstants.TOWER_COIN_ITEM_ID))
|
||||
.setInfo(game.toProto())
|
||||
.setChange(change.toProto());
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public class HandlerStarTowerGiveUpReq extends NetHandler {
|
||||
// Build response
|
||||
var rsp = StarTowerGiveUpResp.newInstance()
|
||||
.setBuild(game.getBuild().toProto())
|
||||
.setFloor(game.getFloor());
|
||||
.setFloor(game.getFloorCount());
|
||||
|
||||
rsp.getMutableChange();
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -12,10 +12,10 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter(AccessLevel.PRIVATE)
|
||||
public class MetaWinHandler implements Handler {
|
||||
public class MetaPatchListHandler implements Handler {
|
||||
private HttpServer server;
|
||||
|
||||
public MetaWinHandler(HttpServer server) {
|
||||
public MetaPatchListHandler(HttpServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ public class RemoteHandler implements Handler {
|
||||
|
||||
// Check admin key
|
||||
if (token.equals(adminKey)) {
|
||||
Nebula.getCommandManager().invoke(null, command);
|
||||
var commandResult = Nebula.getCommandManager().invoke(null, command);
|
||||
Nebula.getLogger().warn("\u001B[38;2;252;186;3mRemote Server (Using Admin Key) sent command: /" + command + "\u001B[0m");
|
||||
ctx.status(200);
|
||||
ctx.contentType(ContentType.APPLICATION_JSON);
|
||||
ctx.result("{\"Code\":200,\"Data\":{},\"Msg\":\"Command executed\"}");
|
||||
ctx.result("{\"Code\":200,\"Data\":{},\"Msg\":\"" + commandResult.getMessage() + "\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ public class RemoteHandler implements Handler {
|
||||
Nebula.getLogger().info("Remote Player Request [" + player.getUid() + "]: " + finalCommand);
|
||||
|
||||
// Execute as console (null sender) but targeting the player
|
||||
Nebula.getCommandManager().invoke(null, finalCommand);
|
||||
var commandResult = Nebula.getCommandManager().invoke(null, finalCommand);
|
||||
|
||||
ctx.status(200);
|
||||
ctx.contentType(ContentType.APPLICATION_JSON);
|
||||
ctx.result("{\"Code\":200,\"Data\":{},\"Msg\":\"Command executed\"}");
|
||||
ctx.result("{\"Code\":200,\"Data\":{},\"Msg\":\"" + commandResult.getMessage() + "\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ import org.bouncycastle.crypto.generators.HKDFBytesGenerator;
|
||||
import org.bouncycastle.crypto.params.*;
|
||||
|
||||
import emu.nebula.Nebula;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import emu.nebula.RegionConfig;
|
||||
|
||||
// Official Name: AeadTool
|
||||
public class AeadHelper {
|
||||
@@ -34,29 +33,31 @@ public class AeadHelper {
|
||||
public static byte[] serverGarbleKey = null;
|
||||
public static byte[] serverMetaKey = null;
|
||||
|
||||
private static final Object2ObjectMap<String, String[]> keys = new Object2ObjectOpenHashMap<>();
|
||||
static {
|
||||
RegionConfig.getRegion("global")
|
||||
.setServerMetaKey("ma5Dn2FhC*Xhxy%c")
|
||||
.setServerGarbleKey("xNdVF^XTa6T3HCUATMQ@sKMLzAw&%L!3");
|
||||
|
||||
RegionConfig.getRegion("kr")
|
||||
.setServerMetaKey("U9cjHuwGDDx&$drn")
|
||||
.setServerGarbleKey("25hdume9H#*6hHn@d9hSF7tekTwN#JYj");
|
||||
|
||||
RegionConfig.getRegion("jp")
|
||||
.setServerMetaKey("ZnUFA@S9%4KyoryM")
|
||||
.setServerGarbleKey("yX5Gt64PVvVH6$qwBXaPJC*LZKoK5mYh");
|
||||
|
||||
RegionConfig.getRegion("tw")
|
||||
.setServerMetaKey("owGYVDmfHrxi^4pm")
|
||||
.setServerGarbleKey("N&mfco452ZH5!nE3s&o5uxB57UGPENVo");
|
||||
}
|
||||
|
||||
public static void loadKeys() {
|
||||
// Load keys
|
||||
keys.put("global", new String[] {
|
||||
"ma5Dn2FhC*Xhxy%c",
|
||||
"xNdVF^XTa6T3HCUATMQ@sKMLzAw&%L!3"
|
||||
});
|
||||
keys.put("kr", new String[] {
|
||||
"U9cjHuwGDDx&$drn",
|
||||
"25hdume9H#*6hHn@d9hSF7tekTwN#JYj"
|
||||
});
|
||||
|
||||
// Get key data
|
||||
var keyData = keys.get(Nebula.getConfig().getRegion().toLowerCase());
|
||||
|
||||
if (keyData == null) {
|
||||
keyData = keys.get("global"); // Default region
|
||||
}
|
||||
var region = RegionConfig.getRegion(Nebula.getConfig().getRegion());
|
||||
|
||||
// Set keys
|
||||
serverMetaKey = keyData[0].getBytes(StandardCharsets.US_ASCII);
|
||||
serverGarbleKey = keyData[1].getBytes(StandardCharsets.US_ASCII);
|
||||
serverMetaKey = region.getServerMetaKey().getBytes(StandardCharsets.US_ASCII);
|
||||
serverGarbleKey = region.getServerGarbleKey().getBytes(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
public static byte[] generateBytes(int size) {
|
||||
|
||||
@@ -17,12 +17,13 @@ import emu.nebula.data.GameData;
|
||||
import emu.nebula.data.ResourceType;
|
||||
import emu.nebula.data.resources.CharacterDef;
|
||||
import emu.nebula.data.resources.ItemDef;
|
||||
import emu.nebula.data.resources.PotentialDef;
|
||||
import emu.nebula.game.inventory.ItemType;
|
||||
|
||||
public class Handbook {
|
||||
|
||||
public static void generate() {
|
||||
// Temp vars
|
||||
Map<String, String> languageKey = null;
|
||||
List<Integer> list = null;
|
||||
|
||||
// Save to file
|
||||
@@ -41,31 +42,64 @@ public class Handbook {
|
||||
writer.println(System.lineSeparator());
|
||||
writer.println("# Characters");
|
||||
list = GameData.getCharacterDataTable().keySet().intStream().sorted().boxed().toList();
|
||||
languageKey = loadLanguageKey(CharacterDef.class);
|
||||
var characterLanguageKey = loadLanguageKey(CharacterDef.class);
|
||||
for (int id : list) {
|
||||
CharacterDef data = GameData.getCharacterDataTable().get(id);
|
||||
writer.print(data.getId());
|
||||
writer.print(" : ");
|
||||
writer.print(languageKey.getOrDefault(data.getName(), data.getName()));
|
||||
writer.print(characterLanguageKey.getOrDefault(data.getName(), data.getName()));
|
||||
writer.print(" (");
|
||||
writer.print(data.getElementType().toString());
|
||||
writer.println(")");
|
||||
}
|
||||
|
||||
// Dump characters
|
||||
// Dump items
|
||||
writer.println(System.lineSeparator());
|
||||
writer.println("# Items");
|
||||
list = GameData.getItemDataTable().keySet().intStream().sorted().boxed().toList();
|
||||
languageKey = loadLanguageKey(ItemDef.class);
|
||||
var itemLanguageKey = loadLanguageKey(ItemDef.class);
|
||||
for (int id : list) {
|
||||
ItemDef data = GameData.getItemDataTable().get(id);
|
||||
writer.print(data.getId());
|
||||
writer.print(" : ");
|
||||
writer.print(languageKey.getOrDefault(data.getTitle(), data.getTitle()));
|
||||
writer.print(itemLanguageKey.getOrDefault(data.getTitle(), data.getTitle()));
|
||||
|
||||
writer.print(" [");
|
||||
writer.print(data.getItemType());
|
||||
writer.println("]");
|
||||
writer.print("]");
|
||||
|
||||
if (data.getItemType() == ItemType.Disc) {
|
||||
var discData = GameData.getDiscDataTable().get(id);
|
||||
if (discData != null) {
|
||||
writer.print(" (");
|
||||
writer.print(discData.getElementType().toString());
|
||||
writer.print(")");
|
||||
}
|
||||
}
|
||||
|
||||
writer.println("");
|
||||
}
|
||||
|
||||
// Dump potentials
|
||||
writer.println(System.lineSeparator());
|
||||
writer.println("# Potentials");
|
||||
list = GameData.getPotentialDataTable().keySet().intStream().sorted().boxed().toList();
|
||||
var potentialLanguageKey = loadLanguageKey(PotentialDef.class);
|
||||
for (int id : list) {
|
||||
PotentialDef data = GameData.getPotentialDataTable().get(id);
|
||||
writer.print(data.getId());
|
||||
writer.print(" : ");
|
||||
|
||||
CharacterDef charData = GameData.getCharacterDataTable().get(data.getCharId());
|
||||
writer.print("[");
|
||||
writer.print(characterLanguageKey.getOrDefault(charData.getName(), charData.getName()));
|
||||
writer.print("] ");
|
||||
|
||||
ItemDef itemData = GameData.getItemDataTable().get(id);
|
||||
writer.print(itemLanguageKey.getOrDefault(itemData.getTitle(), itemData.getTitle()));
|
||||
writer.print(" - ");
|
||||
writer.print(potentialLanguageKey.getOrDefault(data.getBriefDesc(), data.getBriefDesc()));
|
||||
writer.println("");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -173,18 +173,48 @@ public class Utils {
|
||||
public static int randomRange(int min, int max) {
|
||||
return ThreadLocalRandom.current().nextInt(min, max + 1);
|
||||
}
|
||||
|
||||
public static boolean randomChance(double chance) {
|
||||
if (chance <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ThreadLocalRandom.current().nextDouble() < chance;
|
||||
}
|
||||
|
||||
public static int randomElement(int[] array) {
|
||||
return array[ThreadLocalRandom.current().nextInt(0, array.length)];
|
||||
}
|
||||
|
||||
public static <T> T randomElement(List<T> list) {
|
||||
return list.get(ThreadLocalRandom.current().nextInt(0, list.size()));
|
||||
return randomElement(list, false);
|
||||
}
|
||||
|
||||
public static <T> T randomElement(List<T> list, boolean remove) {
|
||||
int index = ThreadLocalRandom.current().nextInt(0, list.size());
|
||||
var object = list.get(index);
|
||||
|
||||
if (remove) {
|
||||
list.remove(index);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
public static int randomElement(IntList list) {
|
||||
return list.getInt(ThreadLocalRandom.current().nextInt(0, list.size()));
|
||||
}
|
||||
|
||||
public static int randomElement(IntList list, boolean remove) {
|
||||
int index = ThreadLocalRandom.current().nextInt(0, list.size());
|
||||
int object = list.getInt(index);
|
||||
|
||||
if (remove) {
|
||||
list.removeInt(index);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an integer array contains a value
|
||||
|
||||
Reference in New Issue
Block a user