Merge remote-tracking branch 'origin/development' into development

# Conflicts:
#	README.md
This commit is contained in:
KingRainbow44
2023-12-05 21:40:51 -05:00
229 changed files with 21308 additions and 878 deletions

View File

@@ -86,13 +86,15 @@ public class Config {
@Getter
public static class ServerOptions {
public boolean autoCreateAccount = true;
public int entitySceneLimit = 2000;
public int sceneMaxEntites = 500;
public boolean spendStamina = true;
public boolean unlockAllChallenges = true;
public int staminaRecoveryRate = 5 * 60;
public int staminaReserveRecoveryRate = 18 * 60;
public String language = "EN";
public Set<String> defaultPermissions = Set.of("*");
public ServerProfile serverFriendInfo = new ServerProfile();
public WelcomeMail welcomeMail = new WelcomeMail();
public int getStaminaRecoveryRate() {
@@ -104,6 +106,17 @@ public class Config {
}
}
@Getter
public static class ServerProfile {
public String name = "Server";
public String signature = "Type /help for a list of commands";
public int level = 1;
public int headIcon = 201001;
public int chatBubbleId = 0;
public int displayAvatarId = 1001;
public int displayAvatarLevel = 1;
}
@Getter
public static class WelcomeMail {
public String title;
@@ -130,6 +143,7 @@ public class Config {
public boolean commands = true;
public boolean connections = true;
public boolean packets = false;
public boolean filterLoopingPackets = false;
}
@Getter

View File

@@ -18,20 +18,33 @@ public class GameConstants {
public static final int MAX_STAMINA = 240;
public static final int MAX_STAMINA_RESERVE = 2400;
public static final int MAX_AVATARS_IN_TEAM = 4;
public static final int MAX_FRIENDSHIPS = 100;
public static final int DEFAULT_TEAMS = 6;
public static final int MAX_MP = 5; // Client doesnt like more than 5
// Chat/Social
public static final int MAX_FRIENDSHIPS = 100;
public static final int MAX_CHAT_HISTORY = 100; // Max chat messages per conversation
// Inventory
public static final int MATERIAL_HCOIN_ID = 1; // Material id for jades. DO NOT CHANGE
public static final int MATERIAL_COIN_ID = 2; // Material id for credits. DO NOT CHANGE
public static final int TRAILBLAZER_EXP_ID = 22;
public static final int INVENTORY_MAX_EQUIPMENT = 1500;
public static final int INVENTORY_MAX_RELIC = 1500;
public static final int INVENTORY_MAX_MATERIAL = 2000;
// Start position
public static final int START_PLANE_ID = 20001;
public static final int START_FLOOR_ID = 20001001;
public static final int START_ENTRY_ID = 2000101;
public static final Position START_POS = new Position(99, 62, -4800);
public static final int MATERIAL_HCOIN_ID = 1; // Material id for jades. DO NOT CHANGE
public static final int MATERIAL_COIN_ID = 2; // Material id for credits. DO NOT CHANGE
public static final int TRAILBLAZER_EXP_ID = 22;
// Battle
public static final int BATTLE_AMBUSH_BUFF_ID = 1000102;
// Gacha
public static final int GACHA_CEILING_MAX = 300; // Yes, I know this is in an excel
// Challenge
public static final int CHALLENGE_ENTRANCE = 100000103;

View File

@@ -255,4 +255,6 @@ public class LunarCore {
return (this.flags & 0x2) == 0x2;
}
}
// Hiro was here
}

View File

@@ -12,4 +12,6 @@ public @interface Command {
public String desc() default "";
public String permission() default "";
public boolean requireTarget() default false;
}

View File

@@ -6,13 +6,17 @@ import emu.lunarcore.LunarCore;
import emu.lunarcore.data.GameData;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.inventory.GameItemSubAffix;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter;
@Getter
public class CommandArgs {
private List<String> list;
private Player sender;
private Player target;
private int targetUid;
@@ -21,10 +25,12 @@ public class CommandArgs {
private int rank = -1;
private int promotion = -1;
private int stage = -1;
private Int2IntMap map;
private static String EMPTY_STRING = "";
public CommandArgs(Player sender, List<String> args) {
this.sender = sender;
this.list = args;
// Parse args. Maybe regex is better.
@@ -35,23 +41,37 @@ public class CommandArgs {
try {
if (arg.length() >= 2 && !Character.isDigit(arg.charAt(0)) && Character.isDigit(arg.charAt(arg.length() - 1))) {
if (arg.startsWith("@")) {
if (arg.startsWith("@")) { // Target UID
this.targetUid = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("x")) {
} else if (arg.startsWith("x")) { // Amount
this.amount = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("lv")) {
} else if (arg.startsWith("lv")) { // Level
this.level = Utils.parseSafeInt(arg.substring(2));
it.remove();
} else if (arg.startsWith("r")) {
} else if (arg.startsWith("r")) { // Rank
this.rank = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("p")) {
} else if (arg.startsWith("e")) { // Eidolons
this.rank = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("p")) { // Promotion
this.promotion = Utils.parseSafeInt(arg.substring(1));
it.remove();
} else if (arg.startsWith("s")) {
} else if (arg.startsWith("s")) { // Stage or Superimposition
this.stage = Utils.parseSafeInt(arg.substring(1));
it.remove();
}
} else if (arg.contains(":")) {
String[] split = arg.split(":");
if (split.length >= 2) {
int key = Integer.parseInt(split[0]);
int value = Integer.parseInt(split[1]);
if (this.map == null) this.map = new Int2IntOpenHashMap();
this.map.put(key, value);
it.remove();
}
}
@@ -85,6 +105,18 @@ public class CommandArgs {
return this.list.get(index);
}
/**
* Sends a message to the command sender
* @param message
*/
public void sendMessage(String message) {
if (sender != null) {
sender.sendMessage(message);
} else {
LunarCore.getLogger().info(message);
}
}
// Utility commands
/**
@@ -144,22 +176,60 @@ public class CommandArgs {
if (this.getLevel() > 0) {
item.setLevel(Math.min(this.getLevel(), 80));
item.setPromotion(Utils.getMinPromotionForLevel(item.getLevel()));
hasChanged = true;
}
// Try to set promotion
if (this.getPromotion() >= 0) {
item.setPromotion(Math.min(this.getPromotion(), item.getExcel().getEquipmentExcel().getMaxPromotion()));
hasChanged = true;
}
// Try to set rank (superimposition)
if (this.getRank() >= 0) {
item.setRank(Math.min(this.getRank(), item.getExcel().getEquipmentExcel().getMaxRank()));
hasChanged = true;
} else if (this.getStage() >= 0) {
item.setRank(Math.min(this.getStage(), item.getExcel().getEquipmentExcel().getMaxRank()));
hasChanged = true;
}
} else if (item.getExcel().isRelic()) {
// Sub stats
if (this.getMap() != null) {
item.resetSubAffixes();
hasChanged = true;
for (var entry : this.getMap().int2IntEntrySet()) {
if (entry.getIntValue() <= 0) continue;
var subAffix = GameData.getRelicSubAffixExcel(item.getExcel().getRelicExcel().getSubAffixGroup(), entry.getIntKey());
if (subAffix == null) continue;
item.getSubAffixes().add(new GameItemSubAffix(subAffix, entry.getIntValue()));
}
}
// Main stat
if (this.getStage() > 0) {
var mainAffix = GameData.getRelicMainAffixExcel(item.getExcel().getRelicExcel().getMainAffixGroup(), this.getStage());
if (mainAffix != null) {
item.setMainAffix(mainAffix.getAffixID());
hasChanged = true;
}
}
// Try to set level
if (this.getLevel() > 0) {
item.setLevel(Math.min(this.getLevel(), 15));
// TODO add substats
// Set relic level
item.setLevel(Math.min(this.getLevel(), 999));
// Apply sub stat upgrades to the relic
int upgrades = item.getMaxNormalSubAffixCount() - item.getCurrentSubAffixCount();
if (upgrades > 0) {
item.addSubAffixes(upgrades);
}
hasChanged = true;
}
}

View File

@@ -1,22 +1,15 @@
package emu.lunarcore.command;
import emu.lunarcore.LunarCore;
import emu.lunarcore.game.player.Player;
public interface CommandHandler {
public default Command getData() {
return this.getClass().getAnnotation(Command.class);
}
public default String getLabel() {
return this.getClass().getAnnotation(Command.class).label();
return getData().label();
}
public default void sendMessage(Player player, String message) {
if (player != null) {
player.sendMessage(message);
} else {
LunarCore.getLogger().info(message);
}
}
public void execute(Player sender, CommandArgs args);
public void execute(CommandArgs args);
}

View File

@@ -39,11 +39,12 @@ public class CommandManager {
}
}
/**
* Adds a command that players and server console users can use. Command handlers must have the proper command annotation attached to them.
*/
public CommandManager registerCommand(CommandHandler handler) {
Command command = handler.getClass().getAnnotation(Command.class);
if (command == null) {
return this;
}
if (command == null) return this;
this.getLabels().put(command.label(), handler);
this.getCommands().put(command.label(), handler);
@@ -55,11 +56,14 @@ public class CommandManager {
return this;
}
/**
* Removes a command from use.
* @param label The command name
* @return
*/
public CommandManager unregisterCommand(String label) {
CommandHandler handler = this.getLabels().get(label);
if (handler == null) {
return this;
}
if (handler == null) return this;
Command command = handler.getClass().getAnnotation(Command.class);
if (command == null) {
@@ -76,6 +80,12 @@ public class CommandManager {
return this;
}
/**
* Checks if the sender has permission to use this command. Will always return true if the sender is the server console.
* @param sender The sender of the command.
* @param command
* @return true if the sender has permission to use this command
*/
private boolean checkPermission(Player sender, Command command) {
if (sender == null || command.permission().isEmpty()) {
return true;
@@ -84,6 +94,12 @@ public class CommandManager {
return sender.getAccount().hasPermission(command.permission());
}
/**
* Checks if the sender has permission to use this command on other players. Will always return true if the sender is the server console.
* @param sender The sender of the command.
* @param command
* @return true if the sender has permission to use this command
*/
private boolean checkTargetPermission(Player sender, Command command) {
if (sender == null || command.permission().isEmpty()) {
return true;
@@ -93,44 +109,59 @@ public class CommandManager {
}
public void invoke(Player sender, String message) {
// Parse message into arguments
List<String> args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new));
// Get command label
String label = args.remove(0).toLowerCase();
// Filter out command prefixes
if (label.startsWith("/") || label.startsWith("!")) {
label = label.substring(1);
}
// Get handler
// Get command handler
CommandHandler handler = this.commands.get(label);
// Execute
// Execute command
if (handler != null) {
// Command annotation data
Command command = handler.getClass().getAnnotation(Command.class);
// Check permission
if (this.checkPermission(sender, command)) {
// Check targeted permission
CommandArgs cmdArgs = new CommandArgs(sender, args);
if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) {
handler.sendMessage(sender, "You do not have permission to use this command on another player.");
return;
}
// Log
if (sender != null && LunarCore.getConfig().getLogOptions().commands) {
LunarCore.getLogger().info("[UID: " + sender.getUid() + "] " + sender.getName() + " used command: " + message);
}
// Run command
handler.execute(sender, cmdArgs);
} else {
handler.sendMessage(sender, "You do not have permission to use this command.");
// Get command annotation data
Command command = handler.getData();
// Check if sender has permission to run the command.
if (sender != null && !this.checkPermission(sender, command)) {
// We have a double null check here just in case
sender.sendMessage("You do not have permission to use this command.");
return;
}
// Build command arguments
CommandArgs cmdArgs = new CommandArgs(sender, args);
// Check targeted permission
if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) {
cmdArgs.sendMessage("You do not have permission to use this command on another player.");
return;
}
// Make sure our command has a target
if (command.requireTarget() && cmdArgs.getTarget() == null) {
cmdArgs.sendMessage("Error: Targeted player not found or offline");
return;
}
// Log
if (sender != null && LunarCore.getConfig().getLogOptions().commands) {
LunarCore.getLogger().info("[UID: " + sender.getUid() + "] " + sender.getName() + " used command: " + message);
}
// Run command
handler.execute(cmdArgs);
} else {
if (sender != null) {
sender.sendMessage("Inavlid Command!");
sender.sendMessage("Invalid Command!");
} else {
LunarCore.getLogger().info("Inavlid Command!");
LunarCore.getLogger().info("Invalid Command!");
}
}
}

View File

@@ -4,16 +4,15 @@ import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.account.AccountHelper;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "account", permission = "admin.account", desc = "/account {create | delete} [username] (reserved player uid). Creates or deletes an account.")
public class AccountCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
public void execute(CommandArgs args) {
if (args.size() < 2) {
this.sendMessage(sender, "Invalid amount of args");
args.sendMessage("Invalid amount of args");
return;
}
@@ -30,16 +29,16 @@ public class AccountCommand implements CommandHandler {
}
if (AccountHelper.createAccount(username, null, reservedUid) != null) {
this.sendMessage(sender, "Account created");
args.sendMessage("Account created");
} else {
this.sendMessage(sender, "Account already exists");
args.sendMessage("Account already exists");
}
}
case "delete" -> {
if (AccountHelper.deleteAccount(username)) {
this.sendMessage(sender, "Account deleted");
args.sendMessage("Account deleted");
} else {
this.sendMessage(sender, "Account doesnt exist");
args.sendMessage("Account doesnt exist");
}
}
}

View File

@@ -1,42 +1,59 @@
package emu.lunarcore.command.commands;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
@Command(label = "avatar", aliases = {"a"}, permission = "player.avatar", desc = "/avatar lv(level) p(ascension) r(eidolon) s(skill levels). Sets the current avatar's properties")
@Command(label = "avatar", aliases = {"a"}, requireTarget = true, permission = "player.avatar", desc = "/avatar {cur | all | lineup} lv(level) p(ascension) r(eidolon) s(skill levels). Sets the current avatar's properties")
public class AvatarCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
public void execute(CommandArgs args) {
// Temp avatar list
List<GameAvatar> changeList = new ArrayList<>();
// Handle optional arguments
switch (args.get(0).toLowerCase()) {
case "all":
args.getTarget().getAvatars().forEach(changeList::add);
break;
case "lineup":
args.getTarget().getCurrentLineup().forEachAvatar(changeList::add);
break;
case "cur":
default:
changeList.add(args.getTarget().getCurrentLeaderAvatar());
break;
}
// Get current leader avatar
GameAvatar avatar = args.getTarget().getCurrentLeaderAvatar();
if (avatar == null) {
// No leader!
return;
}
// Change properties
if (args.setProperties(avatar)) {
// Save avatar
avatar.save();
// Try to set properties of avatars
Iterator<GameAvatar> it = changeList.iterator();
while (it.hasNext()) {
GameAvatar avatar = it.next();
if (args.setProperties(avatar)) {
// Save avatar
avatar.save();
} else {
// Remove from list if nothing was changed
it.remove();
}
}
if (changeList.size() > 0) {
// Send packet
args.getTarget().sendPacket(new PacketPlayerSyncScNotify(avatar));
args.getTarget().sendPacket(new PacketPlayerSyncScNotify(changeList.toArray(GameAvatar[]::new)));
// Send message
sender.sendMessage("Set avatar properties successfully");
args.sendMessage("Set avatar(s) properties successfully");
} else {
sender.sendMessage("No avatar properties to change");
args.sendMessage("No avatar properties to change");
}
}

View File

@@ -8,33 +8,28 @@ import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.enums.ItemMainType;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
@Command(label = "clear", permission = "player.clear", desc = "/clear {relics | lightcones | materials | items}. Removes filtered items from the player inventory.")
@Command(label = "clear", permission = "player.clear", requireTarget = true, desc = "/clear {relics | lightcones | materials | items} lv(filter level). Removes filter items from the targeted player's inventory.")
public class ClearCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
List<GameItem> toRemove = new LinkedList<>();
String type = args.get(0).toLowerCase();
int filterLevel = Math.max(args.getLevel(), 1);
switch (type) {
case "relics", "r" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Relic && item.getLevel() <= 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) {
if (item.getItemMainType() == ItemMainType.Relic && item.getLevel() <= filterLevel && !item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
}
case "equipment", "lightcones", "lc" -> {
for (GameItem item : args.getTarget().getInventory().getItems().values()) {
if (item.getItemMainType() == ItemMainType.Equipment && item.getLevel() <= 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) {
if (item.getItemMainType() == ItemMainType.Equipment && item.getLevel() <= filterLevel && !item.isLocked() && !item.isEquipped()) {
toRemove.add(item);
}
}
@@ -56,7 +51,7 @@ public class ClearCommand implements CommandHandler {
}
args.getTarget().getInventory().removeItems(toRemove);
this.sendMessage(sender, "Removed " + toRemove.size() + " items");
args.sendMessage("Removed " + toRemove.size() + " items");
}
}

View File

@@ -8,17 +8,11 @@ import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.player.PlayerGender;
import emu.lunarcore.server.packet.send.PacketGetHeroBasicTypeInfoScRsp;
@Command(label = "gender", permission = "player.gender", desc = "/gender {male | female}. Sets the player gender.")
@Command(label = "gender", permission = "player.gender", requireTarget = true, desc = "/gender {male | female}. Sets the player gender.")
public class GenderCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
// Set world level
Player target = args.getTarget();
PlayerGender playerGender = null;
@@ -48,9 +42,9 @@ public class GenderCommand implements CommandHandler {
// Send packet and response message
target.sendPacket(new PacketGetHeroBasicTypeInfoScRsp(target));
this.sendMessage(sender, "Gender for " + target.getName() + " set successfully");
args.sendMessage("Gender for " + target.getName() + " set successfully");
} else {
this.sendMessage(sender, "Error: Invalid input");
args.sendMessage("Error: Invalid input");
}
}

View File

@@ -15,22 +15,22 @@ import emu.lunarcore.game.enums.ItemSubType;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
@Command(label = "giveall", aliases = {"ga"}, permission = "player.give", desc = "/giveall {materials | avatars | lightcones | relics}. Gives the targeted player items.")
@Command(
label = "giveall",
aliases = {"ga"},
permission = "player.give",
requireTarget = true,
desc = "/giveall {materials | avatars | lightcones | relics} lv(level). Gives the targeted player items."
)
public class GiveAllCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
Player target = args.getTarget();
String type = args.get(0).toLowerCase();
switch (type) {
default -> this.sendMessage(sender, "Error: Invalid type");
default -> args.sendMessage("Error: Invalid type");
case "m", "materials", "mats" -> {
List<GameItem> items = new ArrayList<>();
@@ -49,9 +49,16 @@ public class GiveAllCommand implements CommandHandler {
target.getInventory().addItems(items, true);
// Send message
this.sendMessage(sender, "Giving " + target.getName() + " " + items.size() + " items");
args.sendMessage("Giving " + target.getName() + " " + items.size() + " items");
}
case "lc", "lightcones" -> {
// Make sure we dont go over the inventory limit
var tab = args.getTarget().getInventory().getTabByItemType(ItemMainType.Equipment);
if (tab.getSize() >= tab.getMaxCapacity()) {
args.sendMessage(target.getName() + " has too many of this item type");
return;
}
// Get lightcones
List<GameItem> items = GameData.getItemExcelMap().values()
.stream()
@@ -67,9 +74,16 @@ public class GiveAllCommand implements CommandHandler {
target.getInventory().addItems(items, true);
// Send message
this.sendMessage(sender, "Giving " + target.getName() + " " + items.size() + " light cones");
args.sendMessage("Giving " + target.getName() + " " + items.size() + " light cones");
}
case "r", "relics" -> {
// Make sure we dont go over the inventory limit
var tab = args.getTarget().getInventory().getTabByItemType(ItemMainType.Relic);
if (tab.getSize() >= tab.getMaxCapacity()) {
args.sendMessage(target.getName() + " has too many of this item type");
return;
}
// Get relics
List<GameItem> items = GameData.getItemExcelMap().values()
.stream()
@@ -80,12 +94,12 @@ public class GiveAllCommand implements CommandHandler {
return item;
})
.toList();
// Add to target's inventory
target.getInventory().addItems(items, true);
// Send message
this.sendMessage(sender, "Giving " + target.getName() + " " + items.size() + " relics");
args.sendMessage("Giving " + target.getName() + " " + items.size() + " relics");
}
case "a", "characters", "avatars" -> {
// All avatars and their eidolons
@@ -111,7 +125,31 @@ public class GiveAllCommand implements CommandHandler {
}
// Send message
this.sendMessage(sender, "Giving " + target.getName() + " all avatars");
args.sendMessage("Giving " + target.getName() + " all avatars");
}
case "ic", "icons" -> {
// Get UnlockedHeads
for (var iconhead : GameData.getPlayerIconExcelMap().values()) {
// This function will handle any duplicate head icons
target.addHeadIcon(iconhead.getId());
}
// Send message
args.sendMessage("Added all icons to " + target.getName());
}
case "consumables", "food" -> {
// Get consumables
List<GameItem> items = GameData.getItemExcelMap().values()
.stream()
.filter(excel -> excel.getItemSubType() == ItemSubType.Food)
.map(excel -> new GameItem(excel, 1000))
.toList();
// Add to target's inventory
target.getInventory().addItems(items, true);
// Send message
args.sendMessage("Added all consumables to " + target.getName());
}
}
}

View File

@@ -11,72 +11,63 @@ import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.enums.ItemMainType;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "give", aliases = {"g"}, permission = "player.give", desc = "/give [item id] x[amount]. Gives the targetted player an item.")
@Command(
label = "give",
aliases = {"g", "item"},
permission = "player.give",
requireTarget = true,
desc = "/give [item id] x(amount) lv(level) r(rank) p(promotion). Gives the targeted player an item."
)
public class GiveCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
int itemId = Utils.parseSafeInt(args.get(0));
int amount = Math.max(args.getAmount(), 1);
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
if (itemData == null) {
this.sendMessage(sender, "Error: Item data not found");
return;
}
public void execute(CommandArgs args) {
// Setup items
List<GameItem> items = new LinkedList<>();
if (itemData.getItemMainType() == ItemMainType.AvatarCard) {
// Add avatar
GameAvatar avatar = new GameAvatar(itemData.getId());
// Get amount to give
int amount = Math.max(args.getAmount(), 1);
// Parse items
for (String arg : args.getList()) {
// Parse item id
int itemId = Utils.parseSafeInt(arg);
if (args.getTarget().addAvatar(avatar)) {
// Change avatar properties
ItemExcel itemData = GameData.getItemExcelMap().get(itemId);
if (itemData == null) {
args.sendMessage("Item \"" + arg + "\" does not exist!");
continue;
}
// Add item
if (itemData.getItemMainType() == ItemMainType.AvatarCard) {
// Add avatar to target
GameAvatar avatar = new GameAvatar(itemData.getId());
args.setProperties(avatar);
}
} else if (itemData.isEquippable()) {
for (int i = 0; i < amount; i++) {
GameItem item = new GameItem(itemData);
args.getTarget().addAvatar(avatar);
} else if (itemData.isEquippable()) {
// Make sure we dont go over the inventory limit
var tab = args.getTarget().getInventory().getTabByItemType(itemData.getItemMainType());
amount = Math.min(amount, tab.getAvailableCapacity());
if (item.getExcel().isEquipment()) {
// Try to set level
if (args.getLevel() > 0) {
item.setLevel(Math.min(args.getLevel(), 80));
item.setPromotion(Utils.getMinPromotionForLevel(item.getLevel()));
}
// Add items
for (int i = 0; i < amount; i++) {
GameItem item = new GameItem(itemData);
args.setProperties(item);
// Try to set promotion
if (args.getPromotion() >= 0) {
item.setPromotion(Math.min(args.getPromotion(), item.getExcel().getEquipmentExcel().getMaxPromotion()));
}
// Try to set rank (superimposition)
if (args.getRank() >= 0) {
item.setRank(Math.min(args.getRank(), item.getExcel().getEquipmentExcel().getMaxRank()));
}
items.add(item);
}
items.add(item);
} else {
items.add(new GameItem(itemData, amount));
}
} else {
items.add(new GameItem(itemData, amount));
// Send message
args.sendMessage("Giving " + args.getTarget().getName() + " " + amount + " of " + itemId);
}
// Add and send message to player
// Add to player inventory
args.getTarget().getInventory().addItems(items, true);
args.getTarget().sendMessage("Giving " + args.getTarget().getName() + " " + amount + " of " + itemId);
}
}

View File

@@ -0,0 +1,23 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.lineup.PlayerLineup;
@Command(label = "heal", permission = "player.heal", requireTarget = true, desc = "/heal. Heals your avatars.")
public class HealCommand implements CommandHandler {
@Override
public void execute(CommandArgs args) {
PlayerLineup lineup = args.getTarget().getCurrentLineup();
lineup.forEachAvatar(avatar -> {
avatar.setCurrentHp(lineup, 10000);
avatar.save();
});
lineup.refreshLineup();
args.sendMessage("Healed all avatars for " + args.getTarget().getName());
}
}

View File

@@ -4,21 +4,20 @@ import emu.lunarcore.LunarCore;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(label = "help", permission = "player.help", desc = "/help. Displays a list of available commands.")
public class HelpCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
this.sendMessage(sender, "Displaying list of commands:");
public void execute(CommandArgs args) {
args.sendMessage("Displaying list of commands:");
var labels = LunarCore.getCommandManager().getLabels().keySet().stream().sorted().toList();
for (var label : labels) {
Command command = LunarCore.getCommandManager().getLabels().get(label).getClass().getAnnotation(Command.class);
if (command == null) continue;
this.sendMessage(sender, command.desc());
args.sendMessage(command.desc());
}
}

View File

@@ -3,27 +3,21 @@ package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(
label = "kick",
desc = "/kick @[player id]. Kicks a player from the server.",
requireTarget = true,
permission = "player.kick"
)
public final class KickCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
// Kick player
args.getTarget().getSession().close();
// Send message
this.sendMessage(sender, "Player kicked successfully");
args.sendMessage("Player kicked successfully");
}
}

View File

@@ -0,0 +1,65 @@
package emu.lunarcore.command.commands;
import java.util.ArrayList;
import java.util.List;
import emu.lunarcore.GameConstants;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.player.lineup.PlayerLineup;
import emu.lunarcore.util.Utils;
@Command(label = "lineup", permission = "player.lineup", requireTarget = true, desc = "/lineup [avatar ids]. USE AT YOUR OWN RISK. Sets your current lineup with the specified avatar ids.")
public class LineupCommand implements CommandHandler {
@Override
public void execute(CommandArgs args) {
// Get target player
Player target = args.getTarget();
// Do not set lineup while the target player is in a battle
if (target.isInBattle()) {
args.sendMessage("Error: The targeted player is in a battle");
return;
}
// Temp avatar list
List<Integer> avatars = new ArrayList<>();
// Validate avatars in temp list
for (String arg : args.getList()) {
// Make sure the avatar actually exist
GameAvatar avatar = target.getAvatarById(Utils.parseSafeInt(arg));
if (avatar == null) continue;
avatars.add(avatar.getAvatarId());
// Soft cap check
if (avatars.size() >= GameConstants.MAX_AVATARS_IN_TEAM) {
break;
}
}
// Replace cleanly
if (avatars.size() > 0) {
// Only replace lineup if we have avatars to replace with
// The client wont like it if we have a lineup with 0 avatars.
PlayerLineup lineup = target.getCurrentLineup();
lineup.getAvatars().clear();
lineup.getAvatars().addAll(avatars);
lineup.save();
// Send client packets to sync lineup
lineup.refreshLineup();
target.getScene().syncLineup();
args.sendMessage("Set the lineup of " + target.getName() + " successfully");
} else {
args.sendMessage("No avatars could be added");
}
}
}

View File

@@ -6,41 +6,27 @@ import java.util.List;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.mail.Mail;
import emu.lunarcore.game.player.Player;
@Command(label = "mail", aliases = {"m"}, permission = "player.mail", desc = "/mail [content]. Sends the targeted player a system mail.")
@Command(label = "mail", aliases = {"m"}, permission = "player.mail", requireTarget = true, desc = "/mail [content]. Sends the targeted player a system mail.")
public class MailCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
// Get attachments
List<GameItem> attachments = new ArrayList<>();
var it = args.getList().iterator();
while (it.hasNext()) {
try {
String str = it.next();
if (args.getMap() != null) {
for (var entry : args.getMap().int2IntEntrySet()) {
if (entry.getIntValue() <= 0) continue;
if (str.contains(":")) {
String[] split = str.split(":");
int itemId = Integer.parseInt(split[0]);
int count = Integer.parseInt(split[1]);
attachments.add(new GameItem(itemId, count));
it.remove();
}
} catch (Exception e) {
ItemExcel itemExcel = GameData.getItemExcelMap().get(entry.getIntKey());
if (itemExcel == null) continue;
attachments.add(new GameItem(itemExcel, entry.getIntValue()));
}
}
@@ -55,7 +41,7 @@ public class MailCommand implements CommandHandler {
// Send to target
args.getTarget().getMailbox().sendMail(mail);
this.sendMessage(sender, "Sending mail to " + args.getTarget().getName());
args.sendMessage("Sending mail to " + args.getTarget().getName());
}
}

View File

@@ -3,19 +3,12 @@ package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(label = "permission", aliases = {"perm"}, permission = "admin.permission", desc = "/permission {add | remove | clear} [permission]. Gives/removes a permission from the targeted player.")
@Command(label = "permission", aliases = {"perm"}, permission = "admin.permission", requireTarget = true, desc = "/permission {add | remove | clear} [permission]. Gives/removes a permission from the targeted player.")
public class PermissionCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
String type = args.get(0).toLowerCase();
String permission = args.get(1).toLowerCase();
@@ -26,7 +19,7 @@ public class PermissionCommand implements CommandHandler {
args.getTarget().getAccount().addPermission(permission);
}
// Send message
this.sendMessage(sender, "Added permission for " + args.getTarget().getName());
args.sendMessage("Added permission for " + args.getTarget().getName());
}
case "remove" -> {
// Remove permission
@@ -34,16 +27,16 @@ public class PermissionCommand implements CommandHandler {
args.getTarget().getAccount().removePermission(permission);
}
// Send message
this.sendMessage(sender, "Removed permission for " + args.getTarget().getName());
args.sendMessage("Removed permission for " + args.getTarget().getName());
}
case "clear" -> {
// Clear permissions
args.getTarget().getAccount().clearPermission();
// Send message
this.sendMessage(sender, "Cleared permissions for " + args.getTarget().getName());
args.sendMessage("Cleared permissions for " + args.getTarget().getName());
}
default -> {
this.sendMessage(sender, "Error: Invalid arguments");
args.sendMessage("Error: Invalid arguments");
}
}
}

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.GameConstants;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
@Command(label = "refill", aliases = {"rf"}, permission = "player.refill", requireTarget = true, desc = "/refill - refill your skill points in open world.")
public class RefillMPCommand implements CommandHandler {
@Override
public void execute(CommandArgs args) {
args.getTarget().getCurrentLineup().addMp(GameConstants.MAX_MP);
args.sendMessage("Successfully refilled skill points for " + args.getTarget().getName());
}
}

View File

@@ -0,0 +1,23 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.game.player.lineup.PlayerLineup;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
@Command(label = "energy", permission = "player.energy", requireTarget = true, desc = "/energy. Refills all characters energy in current lineup.")
public class RefillSPCommand implements CommandHandler {
@Override
public void execute(CommandArgs args) {
PlayerLineup lineup = args.getTarget().getCurrentLineup();
lineup.forEachAvatar(avatar -> {
avatar.setCurrentSp(lineup, 10000);
avatar.save();
});
lineup.refreshLineup();
args.sendMessage("Refilled SP for " + args.getTarget().getName());
}
}

View File

@@ -4,15 +4,14 @@ import emu.lunarcore.LunarCore;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
@Command(label = "reload", permission = "admin.reload", desc = "/reload. Reloads the server config.")
public class ReloadCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
public void execute(CommandArgs args) {
LunarCore.loadConfig();
this.sendMessage(sender, "Reloaded the server config");
args.sendMessage("Reloaded the server config");
}
}

View File

@@ -7,20 +7,13 @@ import emu.lunarcore.data.GameData;
import emu.lunarcore.data.config.AnchorInfo;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.excel.MazePlaneExcel;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "scene", aliases = {"sc"}, permission = "player.scene", desc = "/scene [scene id] [floor id]. Teleports the player to the specified scene.")
@Command(label = "scene", aliases = {"sc"}, permission = "player.scene", requireTarget = true, desc = "/scene [scene id] [floor id]. Teleports the player to the specified scene.")
public class SceneCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
// Get arguments
int planeId = Utils.parseSafeInt(args.get(0));
int floorId = Utils.parseSafeInt(args.get(1));
@@ -28,7 +21,7 @@ public class SceneCommand implements CommandHandler {
// Get maze plane
MazePlaneExcel excel = GameData.getMazePlaneExcelMap().get(planeId);
if (excel == null) {
this.sendMessage(sender, "Error: Maze plane not found");
args.sendMessage("Error: Maze plane not found");
return;
}
@@ -39,7 +32,7 @@ public class SceneCommand implements CommandHandler {
// Get floor info
FloorInfo floor = GameData.getFloorInfo(planeId, floorId);
if (floor == null) {
this.sendMessage(sender, "Error: Floor info not found");
args.sendMessage("Error: Floor info not found");
return;
}
@@ -48,7 +41,7 @@ public class SceneCommand implements CommandHandler {
AnchorInfo anchor = floor.getAnchorInfo(startGroup, anchorId);
if (anchor == null) {
this.sendMessage(sender, "Error: Floor info not found");
args.sendMessage("Error: Floor info not found");
return;
}
@@ -57,7 +50,7 @@ public class SceneCommand implements CommandHandler {
// Send packet
if (success) {
this.sendMessage(sender, "Teleported player to " + planeId);
args.sendMessage("Teleported player to " + planeId);
}
}

View File

@@ -0,0 +1,19 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.util.Utils;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
@Command(label = "setlevel", aliases = {"level"}, permission = "player.setlevel", requireTarget = true, desc = "/setlevel [level] - Sets the targeted player's trailblazer level.")
public class SetLevelCommand implements CommandHandler {
@Override
public void execute(CommandArgs args) {
int targetLevel = Utils.parseSafeInt(args.get(0));
args.getTarget().setLevel(targetLevel);
args.sendMessage("Set level to " + targetLevel);
}
}

View File

@@ -1,5 +1,6 @@
package emu.lunarcore.command.commands;
import emu.lunarcore.LunarCore;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
@@ -16,21 +17,15 @@ import emu.lunarcore.game.scene.entity.EntityProp;
import emu.lunarcore.util.Position;
import emu.lunarcore.util.Utils;
@Command(label = "spawn", permission = "player.spawn", desc = "/spawn [monster/prop id] x[amount] s[stage id]. Spawns a monster or prop near the targeted player.")
@Command(label = "spawn", permission = "player.spawn", requireTarget = true, desc = "/spawn [monster/prop id] [stage id] x[amount] lv[level] r[radius]. Spawns a monster or prop near the targeted player.")
public class SpawnCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
Player target = args.getTarget();
if (target.getScene() == null) {
this.sendMessage(sender, "Error: Target is not in scene");
args.sendMessage("Error: Target is not in scene");
return;
}
@@ -40,6 +35,12 @@ public class SpawnCommand implements CommandHandler {
int amount = Math.max(args.getAmount(), 1);
int radius = Math.max(args.getRank(), 5) * 1000;
// Enforce scene max entity limit
if (target.getScene().getEntities().size() + amount >= LunarCore.getConfig().getServerOptions().getSceneMaxEntites()) {
args.sendMessage("Error: Max entities in scene reached");
return;
}
// Spawn monster
NpcMonsterExcel monsterExcel = GameData.getNpcMonsterExcelMap().get(id);
if (monsterExcel != null) {
@@ -62,7 +63,7 @@ public class SpawnCommand implements CommandHandler {
}
if (monsterInfo == null || groupInfo == null) {
this.sendMessage(sender, "Error: No existing monster config found in this scene");
args.sendMessage("Error: No existing monster config found in this scene");
return;
}
@@ -74,11 +75,15 @@ public class SpawnCommand implements CommandHandler {
monster.setEventId(monsterInfo.getEventID());
monster.setOverrideStageId(stage);
if (args.getLevel() > 0) {
monster.setOverrideLevel(Math.min(args.getLevel(), 100));
}
target.getScene().addEntity(monster, true);
}
// Send message when done
this.sendMessage(sender, "Spawning " + amount + " monsters");
args.sendMessage("Spawning " + amount + " monsters");
return;
}
@@ -103,7 +108,7 @@ public class SpawnCommand implements CommandHandler {
}
if (propInfo == null || groupInfo == null) {
this.sendMessage(sender, "Error: No existing prop config found in this scene");
args.sendMessage("Error: No existing prop config found in this scene");
return;
}
@@ -119,11 +124,11 @@ public class SpawnCommand implements CommandHandler {
}
// Send message when done
this.sendMessage(sender, "Spawning " + amount + " props");
args.sendMessage("Spawning " + amount + " props");
return;
}
this.sendMessage(sender, "Error: Invalid id");
args.sendMessage("Error: Invalid id");
}
}

View File

@@ -10,16 +10,16 @@ import emu.lunarcore.game.player.Player;
public class UnstuckCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
public void execute(CommandArgs args) {
// Make sure were on the game server
if (LunarCore.getGameDatabase() == null) {
this.sendMessage(sender, "Error: Game database not connected");
args.sendMessage("Error: Game database not connected");
return;
}
// TODO add some logic to handle unstucking the target if theyre online
if (args.getTarget() != null) {
this.sendMessage(sender, "Error: Targeted player is online");
args.sendMessage("Error: Targeted player is online");
return;
}
@@ -32,10 +32,10 @@ public class UnstuckCommand implements CommandHandler {
player.save();
// Done
this.sendMessage(sender, "Player unstuck successfully");
args.sendMessage("Player unstuck successfully");
} else {
// Done
this.sendMessage(sender, "Error: Player not found in database");
args.sendMessage("Error: Player not found in database");
}
}

View File

@@ -3,20 +3,13 @@ package emu.lunarcore.command.commands;
import emu.lunarcore.command.Command;
import emu.lunarcore.command.CommandArgs;
import emu.lunarcore.command.CommandHandler;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.util.Utils;
@Command(label = "worldlevel", aliases = {"wl"}, permission = "player.worldlevel", desc = "/worldlevel [world level]. Sets the targeted player's equilibrium level.")
@Command(label = "worldlevel", aliases = {"wl"}, permission = "player.worldlevel", requireTarget = true, desc = "/worldlevel [world level]. Sets the targeted player's equilibrium level.")
public class WorldLevelCommand implements CommandHandler {
@Override
public void execute(Player sender, CommandArgs args) {
// Check target
if (args.getTarget() == null) {
this.sendMessage(sender, "Error: Targeted player not found or offline");
return;
}
public void execute(CommandArgs args) {
// Set world level
int level = Utils.parseSafeInt(args.get(0));
level = Math.min(Math.max(level, 0), 6);
@@ -24,7 +17,7 @@ public class WorldLevelCommand implements CommandHandler {
args.getTarget().setWorldLevel(level);
// Done
this.sendMessage(sender, "Set world level to " + level);
args.sendMessage("Set world level to " + level);
}
}

View File

@@ -2,13 +2,14 @@ package emu.lunarcore.data;
import java.lang.reflect.Field;
import java.util.List;
import java.util.ArrayList;
import emu.lunarcore.data.config.FloorInfo;
import emu.lunarcore.data.excel.*;
import emu.lunarcore.game.battle.MazeBuff;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
@@ -18,10 +19,12 @@ public class GameData {
// Excels
@Getter private static Int2ObjectMap<AvatarExcel> avatarExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ItemExcel> itemExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ItemUseExcel> itemUseExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<EquipmentExcel> equipExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RelicExcel> relicExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<PropExcel> propExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<NpcExcel> npcExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<MonsterExcel> monsterExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<NpcMonsterExcel> npcMonsterExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<StageExcel> stageExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<MazePlaneExcel> mazePlaneExcelMap = new Int2ObjectOpenHashMap<>();
@@ -29,9 +32,15 @@ public class GameData {
@Getter private static Int2ObjectMap<HeroExcel> heroExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ShopExcel> shopExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RewardExcel> rewardExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<PlayerIconExcel> playerIconExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ItemComposeExcel> itemComposeExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ActivityPanelExcel> activityPanelExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<BackGroundMusicExcel> backGroundMusicExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<QuestExcel> questExcelMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static Int2ObjectMap<TextJoinExcel> textJoinExcelMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static Int2ObjectMap<ChatBubbleExcel> chatBubbleExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<PhoneThemeExcel> phoneThemeExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ChallengeGroupExcel> challengeGroupExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ChallengeExcel> challengeExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<ChallengeTargetExcel> challengeTargetExcelMap = new Int2ObjectOpenHashMap<>();
@@ -44,6 +53,7 @@ public class GameData {
@Getter private static Int2ObjectMap<RogueRoomExcel> rogueRoomExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueMapExcel> rogueMapExcelMap = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueMonsterExcel> rogueMonsterExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RogueBuffExcel> rogueBuffExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarPromotionExcel> avatarPromotionExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<AvatarSkillTreeExcel> avatarSkillTreeExcelMap = new Int2ObjectOpenHashMap<>();
@@ -57,9 +67,10 @@ public class GameData {
private static Int2ObjectMap<ExpTypeExcel> expTypeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<EquipmentExpTypeExcel> equipmentExpTypeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicExpTypeExcel> relicExpTypeExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicMainAffixExcel> relicMainAffixExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicSubAffixExcel> relicSubAffixExcelMap = new Int2ObjectOpenHashMap<>();
private static Int2ObjectMap<RelicSetExcel> relicSetExcelMap = new Int2ObjectOpenHashMap<>();
// Configs (Bin)
@Getter private static Object2ObjectMap<String, FloorInfo> floorInfos = new Object2ObjectOpenHashMap<>();
@@ -81,6 +92,77 @@ public class GameData {
return map;
}
public static List<Integer> getAllRelicIds() {
List<Integer> allIds = new ArrayList<>();
for (Int2ObjectMap.Entry<RelicExcel> entry : relicExcelMap.int2ObjectEntrySet()) {
RelicExcel relicExcel = entry.getValue();
allIds.add(relicExcel.getId());
}
return allIds;
}
public static int getRelicSetFromId(int relicId) {
RelicExcel relicExcel = GameData.getRelicExcelMap().get(relicId);
if (relicExcel == null) {
return 0;
}
return relicExcel.getSetId();
}
public static List<Integer> getAllMusicIds() {
List<Integer> allIds = new ArrayList<>();
for (Int2ObjectMap.Entry<BackGroundMusicExcel> entry : backGroundMusicExcelMap.int2ObjectEntrySet()) {
BackGroundMusicExcel backGroundMusicExcel = entry.getValue();
allIds.add(backGroundMusicExcel.getId());
}
return allIds;
}
public static int TextJoinItemFromId(int id) {
for (Int2ObjectMap.Entry<TextJoinExcel> entry : textJoinExcelMap.int2ObjectEntrySet()) {
TextJoinExcel textJoinExcel = entry.getValue();
if (textJoinExcel.getId() == id) {
IntArrayList textJoinItemList = textJoinExcel.getTextJoinItemList();
if (textJoinItemList.size() > 0) {
return textJoinItemList.getInt(textJoinItemList.size() - 1);
}
}
}
return id * 10; // or return a default value if needed
}
public static List<Integer> getAllQuestIds() {
List<Integer> allIds = new ArrayList<>();
for (Int2ObjectMap.Entry<QuestExcel> entry : questExcelMap.int2ObjectEntrySet()) {
QuestExcel questExcel = entry.getValue();
allIds.add(questExcel.getId());
}
return allIds;
}
public static List<Integer> getAllMonsterIds() {
List<Integer> allIds = new ArrayList<>();
for (Int2ObjectMap.Entry<MonsterExcel> entry : monsterExcelMap.int2ObjectEntrySet()) {
MonsterExcel monsterExcel = entry.getValue();
allIds.add(monsterExcel.getId());
}
return allIds;
}
public static int getMusicGroupId(int musicId) {
var excel = backGroundMusicExcelMap.get(musicId);
return excel != null ? excel.getGroupId() : 0;
}
public static AvatarPromotionExcel getAvatarPromotionExcel(int id, int promotion) {
return avatarPromotionExcelMap.get((id << 8) + promotion);
}
@@ -148,4 +230,8 @@ public class GameData {
public static RogueMapExcel getRogueMapExcel(int rogueMapId, int siteId) {
return rogueMapExcelMap.get((rogueMapId << 8) + siteId);
}
public static RogueBuffExcel getRogueBuffExcel(int rogueBuffId, int level) {
return rogueBuffExcelMap.get((rogueBuffId << 4) + level);
}
}

View File

@@ -23,6 +23,8 @@ public class GameDepot {
// Rogue
@Getter private static Int2ObjectMap<int[]> rogueMapGen = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<RogueBuffExcel> rogueAeonBuffs = new Int2ObjectOpenHashMap<>();
@Getter private static Int2ObjectMap<List<RogueBuffExcel>> rogueAeonEnhanceBuffs = new Int2ObjectOpenHashMap<>();
@Getter private static List<RogueBuffExcel> rogueRandomBuffList = new ArrayList<>();
@Getter private static List<RogueMiracleExcel> rogueRandomMiracleList = new ArrayList<>();
@Getter private static List<RogueNPCExcel> rogueRandomNpcList = new ArrayList<>();

View File

@@ -2,7 +2,6 @@ package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@@ -12,7 +11,7 @@ public class ActivityPanelExcel extends GameResource {
private int Type;
private int ActivityModuleID;
private boolean IsResidentPanel;
@Override
public int getId() {
return PanelID;

View File

@@ -40,10 +40,10 @@ public class AvatarExcel extends GameResource {
private transient IntSet skillTreeIds;
private transient String nameKey;
private transient int maxSp;
@Setter private transient MazeSkill mazeAttack;
@Setter private transient MazeSkill mazeSkill;
private static Pattern namePattern = Pattern.compile("(?<=Avatar_)(.*?)(?=_Config)");
public AvatarExcel() {
@@ -75,14 +75,14 @@ public class AvatarExcel extends GameResource {
// Cache max sp
this.maxSp = (int) this.SPNeed * 100;
// Get name key
Matcher matcher = namePattern.matcher(this.JsonPath);
if (matcher.find()) {
this.nameKey = matcher.group(0);
}
// Clear variable to save memory
this.JsonPath = null;
}

View File

@@ -7,7 +7,6 @@ import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@@ -28,7 +27,7 @@ public class AvatarExpItemExcel extends GameResource {
if (excel == null) return;
excel.setAvatarExp(Exp);
// Add to game depot
if (Exp > 0) {
GameDepot.getAvatarExpExcels().add(this);

View File

@@ -41,7 +41,7 @@ public class AvatarSkillTreeExcel extends GameResource {
if (this.isDefaultUnlock()) {
excel.getDefaultSkillTrees().add(this);
}
// Add point id to avatar excel
excel.getSkillTreeIds().add(this.getPointID());
}

View File

@@ -0,0 +1,21 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"BackGroundMusic.json"})
public class BackGroundMusicExcel extends GameResource {
private int ID;
private int GroupID;
@Override
public int getId() {
return ID;
}
public int getGroupId() {
return GroupID;
}
}

View File

@@ -0,0 +1,42 @@
package emu.lunarcore.data.excel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.game.rogue.RogueBuffType;
import emu.lunarcore.util.Utils;
import lombok.Getter;
@Getter
@ResourceType(name = {"BattleEventData.json"})
public class BattleEventDataExcel extends GameResource {
private int BattleEventID;
private String Config;
private static final Pattern roguePattern = Pattern.compile("(?<=Avatar_RogueBattleevent)(.*?)(?=_Config.json)");
@Override
public int getId() {
return BattleEventID;
}
@Override
public void onLoad() {
try {
Matcher matcher = roguePattern.matcher(this.Config);
if (matcher.find()) {
int rogueBuffType = Utils.parseSafeInt(matcher.group(0));
var type = RogueBuffType.getById(rogueBuffType);
if (type != null) {
type.setBattleEventSkill(this.BattleEventID);
}
}
} catch (Exception e) {
// Ignored
}
}
}

View File

@@ -15,46 +15,46 @@ public class ChallengeExcel extends GameResource {
private int StageNum;
private int ChallengeCountDown;
private int MazeBuffID;
private int[] ChallengeTargetID;
private int MazeGroupID1;
private int[] ConfigList1;
private int[] NpcMonsterIDList1;
private int[] EventIDList1;
private int MazeGroupID2;
private int[] ConfigList2;
private int[] NpcMonsterIDList2;
private int[] EventIDList2;
private transient Int2ObjectMap<ChallengeMonsterInfo> challengeMonsters1;
private transient Int2ObjectMap<ChallengeMonsterInfo> challengeMonsters2;
@Override
public int getId() {
return ID;
}
@Override
public void onLoad() {
// Cache challenge monsters
this.challengeMonsters1 = new Int2ObjectOpenHashMap<>();
for (int i = 0; i < ConfigList1.length; i++) {
if (ConfigList1[i] == 0) break;
var monster = new ChallengeMonsterInfo(ConfigList1[i], NpcMonsterIDList1[i], EventIDList1[i]);
this.challengeMonsters1.put(monster.getConfigId(), monster);
}
this.challengeMonsters2 = new Int2ObjectOpenHashMap<>();
for (int i = 0; i < ConfigList2.length; i++) {
if (ConfigList2[i] == 0) break;
var monster = new ChallengeMonsterInfo(ConfigList2[i], NpcMonsterIDList2[i], EventIDList2[i]);
this.challengeMonsters2.put(monster.getConfigId(), monster);
}
// Clear arrays to save memory
this.ConfigList1 = null;
this.NpcMonsterIDList1 = null;
@@ -69,12 +69,12 @@ public class ChallengeExcel extends GameResource {
private int configId;
private int npcMonsterId;
private int eventId;
public ChallengeMonsterInfo(int configId, int npcMonsterId, int eventId) {
this.configId = configId;
this.npcMonsterId = npcMonsterId;
this.eventId = eventId;
}
}
}

View File

@@ -9,7 +9,7 @@ import lombok.Getter;
public class ChallengeGroupExcel extends GameResource {
private int GroupID;
private int RewardLineGroupID;
@Override
public int getId() {
return GroupID;

View File

@@ -10,7 +10,7 @@ public class ChallengeRewardExcel extends GameResource {
private int GroupID;
private int StarCount;
private int RewardID;
@Override
public int getId() {
return (GroupID << 16) + StarCount;

View File

@@ -11,7 +11,7 @@ public class ChallengeTargetExcel extends GameResource {
private int ID;
private ChallengeType ChallengeTargetType;
private int ChallengeTargetParam1;
@Override
public int getId() {
return ID;

View File

@@ -0,0 +1,20 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"ChatBubbleConfig.json"}, loadPriority = LoadPriority.LOW)
public class ChatBubbleExcel extends GameResource {
private int ID;
private String ShowType;
private int ShowParam;
@Override
public int getId() {
return ID;
}
}

View File

@@ -16,7 +16,7 @@ public class CocoonExcel extends GameResource {
private int MaxWave;
private IntArrayList StageIDList;
private IntArrayList DropList;
@Override
public int getId() {
return (ID << 8) + WorldLevel;

View File

@@ -7,7 +7,6 @@ import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@@ -30,7 +29,7 @@ public class EquipmentExpItemExcel extends GameResource {
excel.setEquipmentExp(ExpProvide);
excel.setExpCost(CoinCost);
// Add to game depot
if (ExpProvide > 0) {
GameDepot.getEquipmentExpExcels().add(this);

View File

@@ -10,7 +10,7 @@ import lombok.Getter;
public class HeroExcel extends GameResource {
private int HeroAvatarID;
private PlayerGender Gender;
@Override
public int getId() {
return HeroAvatarID;

View File

@@ -15,11 +15,12 @@ public class ItemComposeExcel extends GameResource {
private int ItemID;
private FormulaType FormulaType;
private List<ItemParam> MaterialCost;
private int[] SpecialMaterialCost;
private IntOpenHashSet SpecialMaterialCost;
private int SpecialMaterialCostNumber;
private int CoinCost;
private int WorldLevelRequire;
private IntOpenHashSet RelicList;
@Override
public int getId() {
return ID;

View File

@@ -8,6 +8,7 @@ import emu.lunarcore.data.common.ItemParam;
import emu.lunarcore.game.enums.ItemMainType;
import emu.lunarcore.game.enums.ItemRarity;
import emu.lunarcore.game.enums.ItemSubType;
import emu.lunarcore.game.enums.ItemUseMethod;
import lombok.Getter;
import lombok.Setter;
@@ -18,11 +19,14 @@ public class ItemExcel extends GameResource {
// General item data
private int ID;
private long ItemName;
private ItemMainType ItemMainType;
private ItemSubType ItemSubType;
private ItemMainType ItemMainType = emu.lunarcore.game.enums.ItemMainType.Unknown;
private ItemSubType ItemSubType = emu.lunarcore.game.enums.ItemSubType.Unknown;
private ItemRarity Rarity;
private int PileLimit;
private int PurposeType;
private int UseDataID;
private ItemUseMethod UseMethod;
private List<ItemParam> ReturnItemIDList;
@@ -44,6 +48,10 @@ public class ItemExcel extends GameResource {
return ItemMainType == emu.lunarcore.game.enums.ItemMainType.Equipment && this.getEquipmentExcel() != null;
}
public boolean isHeadIcon() {
return ItemSubType == emu.lunarcore.game.enums.ItemSubType.HeadIcon;
}
public boolean isRelic() {
return ItemMainType == emu.lunarcore.game.enums.ItemMainType.Relic && this.getRelicExcel() != null;
}
@@ -51,6 +59,10 @@ public class ItemExcel extends GameResource {
public boolean isEquippable() {
return ItemMainType == emu.lunarcore.game.enums.ItemMainType.Relic || ItemMainType == emu.lunarcore.game.enums.ItemMainType.Equipment;
}
public int getRarityNum() {
return this.getRarity().getVal();
}
public int getRelicExp() {
if (this.relicExcel != null) {
@@ -58,7 +70,7 @@ public class ItemExcel extends GameResource {
}
return this.relicExp;
}
public int getRelicExpCost() {
if (this.relicExcel != null) {
return this.relicExcel.getCoinCost();

View File

@@ -0,0 +1,29 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.game.enums.ItemFoodTargetType;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import lombok.Getter;
@Getter
@ResourceType(name = {"ItemUseData.json", "ItemUseBuffData.json"})
public class ItemUseExcel extends GameResource {
private int UseDataID;
private int ConsumeType;
private int MazeBuffID;
private double PreviewHPRecoveryPercent;
private int PreviewHPRecoveryValue;
private double PreviewPowerPercent;
private int PreviewSkillPoint;
private ItemFoodTargetType UseTargetType;
private IntArrayList UseParam;
@Override
public int getId() {
return UseDataID;
}
}

View File

@@ -12,7 +12,7 @@ public class MapEntranceExcel extends GameResource {
private int FloorID;
private int StartGroupID;
private int StartAnchorID;
@Override
public int getId() {
return ID;

View File

@@ -9,7 +9,7 @@ import lombok.Getter;
public class MazeBuffExcel extends GameResource {
private int ID;
private int Lv;
@Override
public int getId() {
return (ID << 4) + Lv;

View File

@@ -14,10 +14,10 @@ public class MazePlaneExcel extends GameResource {
private int WorldID;
private int StartFloorID;
private long PlaneName;
@SerializedName(value = "PlaneType")
private PlaneType planeType = PlaneType.Unknown;
@Override
public int getId() {
return PlaneID;

View File

@@ -13,9 +13,9 @@ public class MonsterDropExcel extends GameResource {
private int MonsterTemplateID;
private int WorldLevel;
private int AvatarExpReward;
private List<ItemParam> DisplayItemList;
@Override
public int getId() {
return (MonsterTemplateID << 4) + WorldLevel;

View File

@@ -0,0 +1,16 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = {"MonsterConfig.json"})
public class MonsterExcel extends GameResource {
private int MonsterID;
@Override
public int getId() {
return MonsterID;
}
}

View File

@@ -8,7 +8,7 @@ import lombok.Getter;
@ResourceType(name = {"NPCData.json"})
public class NpcExcel extends GameResource {
private int ID;
@Override
public int getId() {
return ID;

View File

@@ -0,0 +1,18 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"PhoneThemeConfig.json"}, loadPriority = LoadPriority.LOW)
public class PhoneThemeExcel extends GameResource {
private int ID;
private String ShowType;
@Override
public int getId() {
return ID;
}
}

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"PlayerIcon.json"}, loadPriority = LoadPriority.NORMAL)
public class PlayerIconExcel extends GameResource {
private int ID;
@Override
public int getId() {
return ID;
}
}

View File

@@ -12,15 +12,15 @@ public class PropExcel extends GameResource {
private long PropName;
private String JsonPath;
private PropType PropType;
private transient boolean recoverHp;
private transient boolean recoverMp;
@Override
public int getId() {
return ID;
}
@Override
public void onLoad() {
// Hacky way to determine if a prop will recover hp or mp
@@ -31,7 +31,7 @@ public class PropExcel extends GameResource {
this.recoverHp = true;
}
}
// Clear for optimization
this.JsonPath = null;
}

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"QuestData.json"}, loadPriority = LoadPriority.NORMAL)
public class QuestExcel extends GameResource {
private int QuestID;
@Override
public int getId() {
return QuestID;
}
}

View File

@@ -26,6 +26,9 @@ public class RelicExcel extends GameResource {
public int getId() {
return ID;
}
public int getSetId() {
return SetID;
}
@Override
public void onLoad() {

View File

@@ -30,7 +30,7 @@ public class RelicExpItemExcel extends GameResource {
excel.setRelicExp(ExpProvide);
excel.setExpCost(CoinCost);
// Add to game depot
if (ExpProvide > 0 && excel.getRarity() != ItemRarity.SuperRare) {
GameDepot.getRelicExpExcels().add(this);

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType(name = {"RelicSetConfig.json"}, loadPriority = LoadPriority.NORMAL)
public class RelicSetExcel extends GameResource {
private int SetID;
@Override
public int getId() {
return SetID;
}
}

View File

@@ -13,9 +13,9 @@ import lombok.Getter;
@ResourceType(name = {"RewardData.json"})
public class RewardExcel extends GameResource {
private int RewardID;
private int Hcoin;
private int ItemID_1;
private int Count_1;
private int ItemID_2;
@@ -26,9 +26,9 @@ public class RewardExcel extends GameResource {
private int Count_4;
private int ItemID_5;
private int Count_5;
private transient List<ItemParam> rewards;
@Override
public int getId() {
return RewardID;
@@ -37,11 +37,11 @@ public class RewardExcel extends GameResource {
@Override
public void onLoad() {
this.rewards = new ArrayList<>();
if (Hcoin > 0) {
this.rewards.add(new ItemParam(GameConstants.MATERIAL_HCOIN_ID, Hcoin));
}
if (ItemID_1 > 0) {
this.rewards.add(new ItemParam(ItemID_1, Count_1));
} if (ItemID_2 > 0) {

View File

@@ -9,7 +9,7 @@ import lombok.Getter;
public class RogueAeonExcel extends GameResource {
private int AeonID;
private int RogueBuffType;
@Override
public int getId() {
return AeonID;

View File

@@ -6,6 +6,7 @@ import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter;
@Getter
@@ -14,10 +15,11 @@ public class RogueAreaExcel extends GameResource {
private int RogueAreaID;
private int AreaProgress;
private int Difficulty;
private Int2IntOpenHashMap ScoreMap;
private transient int mapId;
private transient List<RogueMapExcel> sites;
@Override
public int getId() {
return RogueAreaID;

View File

@@ -1,29 +1,45 @@
package emu.lunarcore.data.excel;
import java.util.ArrayList;
import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import emu.lunarcore.data.ResourceType.LoadPriority;
import emu.lunarcore.game.enums.RogueBuffAeonType;
import lombok.Getter;
@Getter
@ResourceType(name = {"RogueBuff.json"})
@ResourceType(name = {"RogueBuff.json"}, loadPriority = LoadPriority.LOW)
public class RogueBuffExcel extends GameResource {
private int MazeBuffID;
private int MazeBuffLevel;
private int RogueBuffType;
private int RogueBuffRarity;
private int AeonID;
private RogueBuffAeonType BattleEventBuffType = RogueBuffAeonType.Normal;
@Override
public int getId() {
return MazeBuffID;
return (MazeBuffID << 4) + MazeBuffLevel;
}
public boolean isAeonBuff() {
return this.BattleEventBuffType != RogueBuffAeonType.Normal;
}
@Override
public void onLoad() {
// Add to random buff list
if (RogueBuffType >= 120 && RogueBuffType <= 126 && RogueBuffRarity >= 1 && RogueBuffRarity <= 3 && MazeBuffLevel == 1 && AeonID == 0) {
GameDepot.getRogueRandomBuffList().add(this);
}
// Add to aeon buff list
if (BattleEventBuffType == RogueBuffAeonType.BattleEventBuff) {
GameDepot.getRogueAeonBuffs().put(this.getAeonID(), this);
} else if (BattleEventBuffType == RogueBuffAeonType.BattleEventBuffEnhance) {
GameDepot.getRogueAeonEnhanceBuffs().computeIfAbsent(this.getAeonID(), e -> new ArrayList<>()).add(this);
}
}
}

View File

@@ -1,6 +1,7 @@
package emu.lunarcore.data.excel;
import java.time.*;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import emu.lunarcore.data.GameResource;
@@ -12,20 +13,20 @@ import lombok.Getter;
@ResourceType(name = {"RogueManager.json"})
public class RogueManagerExcel extends GameResource {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private int ScheduleDataID;
private int RogueSeason;
@Getter(AccessLevel.NONE)
private String BeginTime;
@Getter(AccessLevel.NONE)
private String EndTime;
private int[] RogueAreaIDList;
private transient long beginTime;
private transient long endTime;
@Override
public int getId() {
return ScheduleDataID;
@@ -38,13 +39,13 @@ public class RogueManagerExcel extends GameResource {
.atOffset(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
this.endTime = LocalDateTime.from(formatter.parse(this.EndTime))
.atOffset(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
} catch (Exception e) {
}
}
}

View File

@@ -14,12 +14,12 @@ public class RogueMapExcel extends GameResource {
private boolean IsStart;
private int[] NextSiteIDList;
private int[] LevelList;
@Override
public int getId() {
return (RogueMapID << 8) + SiteID;
}
@Override
public void onLoad() {
GameDepot.getRogueMapsById(this.getRogueMapID()).add(this);

View File

@@ -3,7 +3,6 @@ package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameDepot;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@@ -12,7 +11,7 @@ public class RogueMiracleExcel extends GameResource {
private int MiracleID;
private boolean IsShow;
private int MiracleReward;
@Override
public int getId() {
return MiracleID;

View File

@@ -2,7 +2,6 @@ package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import lombok.Getter;
@Getter
@@ -11,7 +10,7 @@ public class RogueMonsterExcel extends GameResource {
private int RogueMonsterID;
private int NpcMonsterID;
private int EventID;
@Override
public int getId() {
return RogueMonsterID;

View File

@@ -10,7 +10,7 @@ import lombok.Getter;
public class RogueNPCExcel extends GameResource {
private int RogueNPCID;
private int NPCID;
@Override
public int getId() {
return RogueNPCID;

View File

@@ -13,7 +13,7 @@ public class RogueRoomExcel extends GameResource {
private int MapEntrance;
private int GroupID;
private Int2IntOpenHashMap GroupWithContent;
@Override
public int getId() {
return RogueRoomID;

View File

@@ -12,7 +12,7 @@ import lombok.Getter;
public class RogueTalentExcel extends GameResource {
private int TalentID;
private List<ItemParam> Cost;
@Override
public int getId() {
return TalentID;

View File

@@ -2,8 +2,8 @@ package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@Getter
@@ -11,13 +11,13 @@ import lombok.Getter;
public class ShopExcel extends GameResource {
private int ShopID;
private int ShopType;
private transient Int2ObjectMap<ShopGoodsExcel> goods;
public ShopExcel() {
this.goods = new Int2ObjectOpenHashMap<>();
this.goods = new Int2ObjectAVLTreeMap<>();
}
@Override
public int getId() {
return ShopID;

View File

@@ -19,38 +19,41 @@ public class ShopGoodsExcel extends GameResource {
private int ItemID;
private int ItemCount;
private int ShopID;
@Getter(AccessLevel.NONE)
private int[] CurrencyList;
@Getter(AccessLevel.NONE)
private int[] CurrencyCostList;
private transient List<ItemParam> costList;
@Override
public int getId() {
return GoodsID;
}
@Override
public void onLoad() {
// Skip if we dont have an item id associated with this goods excel
if (this.getItemID() == 0) return;
// Add to shop excel
ShopExcel shop = GameData.getShopExcelMap().get(this.ShopID);
if (shop == null) return;
shop.getGoods().put(this.GoodsID, this);
// Cache currency cost
this.costList = new ArrayList<>(CurrencyList.length);
for (int i = 0; i < CurrencyList.length; i++) {
ItemParam param = new ItemParam(CurrencyList[i], CurrencyCostList[i]);
this.costList.add(param);
}
// Done - Clear references to save memory
this.CurrencyList = null;
this.CurrencyCostList = null;
this.CurrencyCostList = null;
}
public Goods toProto() {
@@ -58,7 +61,7 @@ public class ShopGoodsExcel extends GameResource {
.setGoodsId(this.getGoodsID())
.setItemId(this.getItemID())
.setEndTime(Integer.MAX_VALUE);
return proto;
}
}

View File

@@ -18,10 +18,10 @@ public class StageExcel extends GameResource {
private long StageName;
private StageType StageType;
private int Level;
@Getter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private List<StageMonsterWave> MonsterList;
// Cache
private transient List<IntList> monsterWaves;
@@ -38,18 +38,18 @@ public class StageExcel extends GameResource {
this.monsterWaves.add(wave.toList());
}
}
public static class StageMonsterWave {
private int Monster0;
private int Monster1;
private int Monster2;
private int Monster3;
private int Monster4;
// Sigh...
public IntList toList() {
IntList list = new IntArrayList(5);
if (this.Monster0 != 0) {
list.add(this.Monster0);
} if (this.Monster1 != 0) {
@@ -61,7 +61,7 @@ public class StageExcel extends GameResource {
} if (this.Monster4 != 0) {
list.add(this.Monster4);
}
return list;
}
}

View File

@@ -0,0 +1,23 @@
package emu.lunarcore.data.excel;
import emu.lunarcore.data.GameResource;
import emu.lunarcore.data.ResourceType;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import lombok.Getter;
@Getter
@ResourceType(name = {"TextJoinConfig.json"})
public class TextJoinExcel extends GameResource {
private int TextJoinID;
private int DefaultItem;
private IntArrayList TextJoinItemList;
@Override
public int getId() {
return TextJoinID;
}
public IntArrayList getTextJoinItemList() {
return TextJoinItemList;
}
}

View File

@@ -48,15 +48,15 @@ public final class DatabaseManager {
// Local mongo server
if (info.isUseInternal() && Utils.isPortOpen(internalConfig.getAddress(), internalConfig.getPort())) {
connectionString = startInternalMongoServer(internalConfig);
LunarCore.getLogger().info("Using local mongo server at " + server.getConnectionString());
LunarCore.getLogger().info("Started local mongo server at " + server.getConnectionString());
}
// Initialize
MongoClient gameMongoClient = MongoClients.create(connectionString);
MongoClient mongoClient = MongoClients.create(connectionString);
// Add our custom fastutil codecs
var codecProvider = CodecRegistries.fromCodecs(
new IntSetCodec(), new Int2IntMapCodec()
new IntSetCodec(), new IntListCodec(), new Int2IntMapCodec()
);
// Set mapper options.
@@ -67,7 +67,7 @@ public final class DatabaseManager {
.build();
// Create data store.
datastore = Morphia.createDatastore(gameMongoClient, info.getCollection(), mapperOptions);
datastore = Morphia.createDatastore(mongoClient, info.getCollection(), mapperOptions);
// Map classes
var entities = new Reflections(LunarCore.class.getPackageName())

View File

@@ -0,0 +1,42 @@
package emu.lunarcore.database.codecs;
import org.bson.BsonReader;
import org.bson.BsonType;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
/**
* Custom mongodb codec for encoding/decoding fastutil int sets.
*/
public class IntListCodec implements Codec<IntList> {
@Override
public Class<IntList> getEncoderClass() {
return IntList.class;
}
@Override
public void encode(BsonWriter writer, IntList collection, EncoderContext encoderContext) {
writer.writeStartArray();
for (int value : collection) {
writer.writeInt32(value);
}
writer.writeEndArray();
}
@Override
public IntList decode(BsonReader reader, DecoderContext decoderContext) {
IntList collection = new IntArrayList();
reader.readStartArray();
while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
collection.add(reader.readInt32());
}
reader.readEndArray();
return collection;
}
}

View File

@@ -53,15 +53,20 @@ public class AvatarStorage extends BasePlayerManager implements Iterable<GameAva
// Set owner first
avatar.setOwner(getPlayer());
// Put into maps
// Put into avatar map
this.avatars.put(avatar.getAvatarId(), avatar);
// Save to database
// Save to database and send packet
avatar.save();
// Send packet
getPlayer().sendPacket(new PacketPlayerSyncScNotify(avatar));
// Add head icon
int headIconId = 200000 + avatar.getAvatarId();
if (GameData.getItemExcelMap().containsKey(headIconId)) {
getPlayer().addHeadIcon(headIconId);
}
// Done
return true;
}

View File

@@ -47,7 +47,7 @@ public class GameAvatar implements GameEntity {
private transient Player owner;
private transient AvatarExcel excel;
private int avatarId; // Id of avatar
private int avatarId; // Id of avatar in the excels
private AvatarData data;
@Setter private int level;
@Setter private int exp;

View File

@@ -13,11 +13,12 @@ import emu.lunarcore.game.inventory.GameItem;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.game.player.lineup.PlayerLineup;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.ClientTurnSnapshotOuterClass.ClientTurnSnapshot;
import emu.lunarcore.proto.SceneBattleInfoOuterClass.SceneBattleInfo;
import emu.lunarcore.proto.SceneMonsterOuterClass.SceneMonster;
import emu.lunarcore.proto.SceneMonsterWaveOuterClass.SceneMonsterWave;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
import lombok.Setter;
@@ -33,6 +34,8 @@ public class Battle {
private final List<GameItem> drops;
private final long timestamp;
private IntList turnSnapshotList; // TODO maybe turn it into a map?
@Setter private int staminaCost;
@Setter private int levelOverride;
@Setter private int roundsLimit;
@@ -58,6 +61,13 @@ public class Battle {
this.stages.addAll(stages);
}
public IntList getTurnSnapshotList() {
if (this.turnSnapshotList == null) {
this.turnSnapshotList = new IntArrayList();
}
return this.turnSnapshotList;
}
public StageType getStageType() {
StageExcel stage = this.getFirstStage();
if (stage != null) {
@@ -111,6 +121,10 @@ public class Battle {
return buff;
}
public boolean hasBuff(int buffId) {
return this.buffs.stream().filter(buff -> buff.getId() == buffId).findFirst().isPresent();
}
public void clearBuffs() {
this.buffs.clear();
}
@@ -175,11 +189,33 @@ public class Battle {
}
}
// Apply food buffs to battle
if (player.getFoodBuffs().size() > 0) {
for (int buffId : player.getFoodBuffs().values()) {
this.addBuff(buffId, -1);
}
}
// Buffs
for (MazeBuff buff : this.getBuffs()) {
proto.addBuffList(buff.toProto());
}
// Client turn snapshots
if (this.turnSnapshotList != null) {
for (int id : this.turnSnapshotList) {
var snapshot = ClientTurnSnapshot.newInstance()
.setBattleEventId(id);
// Temp solution
snapshot.getMutableStatus().getMutableSpBar()
.setCurSp(10000)
.setMaxSp(10000);
proto.addTurnSnapshotList(snapshot);
}
}
return proto;
}
}

View File

@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import emu.lunarcore.GameConstants;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.CocoonExcel;
import emu.lunarcore.data.excel.StageExcel;
@@ -63,13 +64,13 @@ public class BattleService extends BaseGameService {
}
}
// Give the client an error if no attacked entities detected
// Skip if no attacked entities detected
if (targetEntities.size() == 0) {
player.sendPacket(new PacketSceneCastSkillScRsp());
player.sendPacket(new PacketSceneCastSkillScRsp(attackedGroupId));
return;
}
// Monster list
// Separate entities into monster list
List<EntityMonster> monsters = new ArrayList<>();
// Destroy props
@@ -84,6 +85,15 @@ public class BattleService extends BaseGameService {
player.getScene().removeEntity(entity);
}
}
// Check if we are using a skill that doesnt trigger a battle
if (castedSkill != null && !castedSkill.isTriggerBattle()) {
// Apply buffs to monsters
castedSkill.onAttack(player.getCurrentLeaderAvatar(), monsters);
// Skip battle if our technique does not trigger a battle
player.sendPacket(new PacketSceneCastSkillScRsp(attackedGroupId));
return;
}
// Start battle
if (monsters.size() > 0) {
@@ -106,7 +116,20 @@ public class BattleService extends BaseGameService {
// Create battle and add npc monsters to it
Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), stages);
battle.getNpcMonsters().addAll(monsters);
// Add npc monsters
for (var monster : monsters) {
battle.getNpcMonsters().add(monster);
// Handle monster buffs
// TODO handle multiple waves properly
monster.applyBuffs(battle);
// Override level
if (monster.getOverrideLevel() > 0) {
battle.setLevelOverride(monster.getOverrideLevel());
}
}
// Add buffs to battle
if (isPlayerCaster) {
@@ -122,6 +145,9 @@ public class BattleService extends BaseGameService {
buff.addDynamicValue("SkillIndex", castedSkill.getIndex());
}
}
} else {
// Ambush buff (for monsters)
battle.addBuff(GameConstants.BATTLE_AMBUSH_BUFF_ID, -1, 1);
}
// Challenge
@@ -274,6 +300,11 @@ public class BattleService extends BaseGameService {
}
}
// Clear food buffs for player
if (player.getFoodBuffs().size() > 0) {
player.getFoodBuffs().clear();
}
// Challenge
if (player.getChallengeInstance() != null) {
player.getChallengeInstance().onBattleFinish(battle, result, stats);

View File

@@ -1,6 +1,7 @@
package emu.lunarcore.game.battle;
import emu.lunarcore.data.excel.MazeBuffExcel;
import emu.lunarcore.game.scene.entity.GameEntity;
import emu.lunarcore.proto.BattleBuffOuterClass.BattleBuff;
import emu.lunarcore.proto.BattleBuffOuterClass.BattleBuff.DynamicValuesEntry;
import it.unimi.dsi.fastutil.ints.IntArrayList;
@@ -9,6 +10,7 @@ import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Getter
public class MazeBuff {
@@ -19,7 +21,10 @@ public class MazeBuff {
private IntList targetIndex;
@Getter(AccessLevel.NONE)
private Object2DoubleMap<String> dynamicValues;
private Object2DoubleMap<String> dynamicValues;
@Setter
private transient GameEntity owner;
public MazeBuff(MazeBuffExcel excel, int ownerIndex, int waveFlag) {
this(excel.getBuffId(), excel.getLv(), ownerIndex, waveFlag);
@@ -52,9 +57,12 @@ public class MazeBuff {
var proto = BattleBuff.newInstance()
.setId(this.getId())
.setLevel(this.getLevel())
.setOwnerId(this.getOwnerIndex())
.setWaveFlag(this.getWaveFlag());
if (this.ownerIndex != 0) {
proto.setOwnerId(this.getOwnerIndex());
}
if (this.targetIndex != null) {
for (int index : this.targetIndex) {
proto.addTargetIndexList(index);

View File

@@ -6,6 +6,7 @@ import java.util.List;
import emu.lunarcore.data.excel.AvatarExcel;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import lombok.Getter;
import lombok.Setter;
@@ -44,4 +45,13 @@ public class MazeSkill {
action.onAttack(caster, battle);
}
}
// Triggered when player attacks an enemy
public void onAttack(GameAvatar caster, List<EntityMonster> monsters) {
if (this.getAttackActions().size() == 0) return;
for (var action : this.getAttackActions()) {
action.onAttack(caster, monsters);
}
}
}

View File

@@ -1,7 +1,10 @@
package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public abstract class MazeSkillAction {
@@ -10,4 +13,6 @@ public abstract class MazeSkillAction {
public abstract void onAttack(GameAvatar caster, Battle battle);
public abstract void onAttack(GameAvatar caster, List<EntityMonster> monsters);
}

View File

@@ -1,7 +1,10 @@
package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
import lombok.Getter;
@@ -29,5 +32,12 @@ public class MazeSkillAddBuff extends MazeSkillAction {
battle.addBuff(buffId, battle.getLineup().getLeader(), 1 << i);
}
}
@Override
public void onAttack(GameAvatar caster, List<EntityMonster> monsters) {
for (EntityMonster monster : monsters) {
monster.addBuff(caster.getAvatarId(), buffId, duration);
}
}
}

View File

@@ -1,7 +1,10 @@
package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public class MazeSkillModifyHP extends MazeSkillAction {
@@ -21,4 +24,9 @@ public class MazeSkillModifyHP extends MazeSkillAction {
}
@Override
public void onAttack(GameAvatar caster, List<EntityMonster> monsters) {
}
}

View File

@@ -1,7 +1,10 @@
package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public class MazeSkillModifySP extends MazeSkillAction {
@@ -24,4 +27,9 @@ public class MazeSkillModifySP extends MazeSkillAction {
}
@Override
public void onAttack(GameAvatar caster, List<EntityMonster> monsters) {
}
}

View File

@@ -1,7 +1,10 @@
package emu.lunarcore.game.battle.skills;
import java.util.List;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.battle.Battle;
import emu.lunarcore.game.scene.entity.EntityMonster;
import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo;
public class MazeSkillSummonUnit extends MazeSkillAction {
@@ -16,4 +19,9 @@ public class MazeSkillSummonUnit extends MazeSkillAction {
// TODO Auto-generated method stub
}
@Override
public void onAttack(GameAvatar caster, List<EntityMonster> monsters) {
// TODO Auto-generated method stub
}
}

View File

@@ -14,6 +14,7 @@ import emu.lunarcore.proto.ChallengeStatusOuterClass.ChallengeStatus;
import emu.lunarcore.proto.ExtraLineupTypeOuterClass.ExtraLineupType;
import emu.lunarcore.server.packet.send.PacketChallengeLineupNotify;
import emu.lunarcore.server.packet.send.PacketChallengeSettleNotify;
import emu.lunarcore.server.packet.send.PacketSyncLineupNotify;
import emu.lunarcore.util.Position;
import lombok.Getter;
@@ -32,6 +33,7 @@ public class ChallengeInstance {
private int status;
private boolean hasAvatarDied;
@Setter private int savedMp;
@Setter private int roundsLeft;
@Setter private int stars;
@@ -79,7 +81,8 @@ public class ChallengeInstance {
}
public void onBattleFinish(Battle battle, BattleEndStatus result, BattleStatistics stats) {
if (result == BattleEndStatus.BATTLE_END_WIN) {
switch (result) {
case BATTLE_END_WIN:
// Check if any avatar in the lineup has died
battle.getLineup().forEachAvatar(avatar -> {
if (avatar.getCurrentHp(battle.getLineup()) <= 0) {
@@ -109,6 +112,7 @@ public class ChallengeInstance {
this.setCurrentExtraLineup(ExtraLineupType.LINEUP_CHALLENGE_2);
player.getLineupManager().setCurrentExtraLineup(this.getCurrentExtraLineup(), true);
player.sendPacket(new PacketChallengeLineupNotify(this.getCurrentExtraLineup()));
this.savedMp = player.getCurrentLineup().getMp();
// Move player
player.moveTo(this.getStartPos(), this.getStartRot());
}
@@ -116,9 +120,21 @@ public class ChallengeInstance {
// Calculate rounds left
this.roundsLeft = Math.min(Math.max(this.roundsLeft - stats.getRoundCnt(), 1), this.roundsLeft);
} else {
// Set saved technique points (This will be restored if the player resets the challenge)
this.savedMp = player.getCurrentLineup().getMp();
break;
case BATTLE_END_QUIT:
// Reset technique points and move back to start position
var lineup = player.getCurrentLineup();
lineup.setMp(this.savedMp);
player.moveTo(this.getStartPos(), this.getStartRot());
player.sendPacket(new PacketSyncLineupNotify(lineup));
break;
default:
// Fail challenge
this.setStatus(ChallengeStatus.CHALLENGE_FAILED);
break;
}
}

View File

@@ -86,6 +86,7 @@ public class ChallengeManager extends BasePlayerManager {
// Save start positions
instance.getStartPos().set(getPlayer().getPos());
instance.getStartRot().set(getPlayer().getRot());
instance.setSavedMp(getPlayer().getCurrentLineup().getMp());
// Send packet
getPlayer().sendPacket(new PacketStartChallengeScRsp(getPlayer(), challengeId));

View File

@@ -0,0 +1,16 @@
package emu.lunarcore.game.enums;
import lombok.Getter;
@Getter
public enum ItemFoodTargetType {
All (0),
Alive (101),
Dead (102);
private int val;
private ItemFoodTargetType(int value) {
this.val = value;
}
}

View File

@@ -1,22 +1,29 @@
package emu.lunarcore.game.enums;
import emu.lunarcore.game.inventory.tabs.InventoryTabType;
import lombok.Getter;
@Getter
public enum ItemMainType {
Unknown (0),
Virtual (1),
AvatarCard (2),
Equipment (3),
Relic (4),
Usable (5),
Material (6),
Mission (7),
Display (8);
Unknown (0),
Virtual (1),
AvatarCard (2),
Equipment (3, InventoryTabType.EQUIPMENT),
Relic (4, InventoryTabType.RELIC),
Usable (5, InventoryTabType.MATERIAL),
Material (6, InventoryTabType.MATERIAL),
Mission (7, InventoryTabType.MATERIAL),
Display (8);
private int val;
private InventoryTabType tabType;
private ItemMainType(int value) {
this.val = value;
}
private ItemMainType(int value, InventoryTabType tabType) {
this.val = value;
this.tabType = tabType;
}
}

View File

@@ -0,0 +1,29 @@
package emu.lunarcore.game.enums;
import lombok.Getter;
@Getter
public enum ItemUseMethod {
Unknown (0),
FixedRewardGift (101),
RandomRewardGift (102),
PlayerSelectedReward (103),
TeamFoodBenefit (201),
TeamSpecificFoodBenefit (202),
ExternalSystemFoodBenefit (203),
PlayerSelectedDropGift (301),
TreasureMap (401),
Recipe (501),
PerformanceProp (601),
MonthlyCard (701),
BPUnlock68 (702),
BPUnlock128 (703),
BPUpgradeFrom68To128 (704),
AutoConversionItem (801);
private int val;
private ItemUseMethod(int value) {
this.val = value;
}
}

View File

@@ -0,0 +1,17 @@
package emu.lunarcore.game.enums;
import lombok.Getter;
@Getter
public enum RogueBuffAeonType {
Normal (0),
BattleEventBuff (1),
BattleEventBuffEnhance (2),
BattleEventBuffCross (3);
private final int val;
private RogueBuffAeonType(int value) {
this.val = value;
}
}

View File

@@ -1,6 +1,6 @@
package emu.lunarcore.game.gacha;
import emu.lunarcore.proto.GachaCeilingOuterClass.GachaCeiling;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.GachaInfoOuterClass.GachaInfo;
import lombok.Getter;
@@ -14,30 +14,34 @@ public class GachaBanner {
private int[] rateUpItems4;
private int eventChance = 50;
public GachaInfo toProto() {
public GachaInfo toProto(GachaService service, Player player) {
var info = GachaInfo.newInstance()
.setGachaId(this.getId())
.setDetailUrl("")
.setHistoryUrl("");
if (this.gachaType == GachaType.Normal) {
// Gacha ceiling
info.setGachaCeiling(GachaCeiling.newInstance());
} else {
if (this.gachaType != GachaType.Normal) {
info.setBeginTime(this.getBeginTime());
info.setEndTime(this.getEndTime());
}
if (this.getRateUpItems4().length > 0) {
for (int id : getRateUpItems4()) {
info.addUpInfo(id);
if (this.getId() == 1001) {
info.setGachaCeiling(player.getGachaInfo().toGachaCeiling(player));
info.addAllUpInfo(service.getPurpleAvatars());
info.addAllUpInfo(service.getYellowAvatars());
info.addAllUpInfo(service.getPurpleWeapons());
info.addAllUpInfo(service.getYellowWeapons());
info.addAllFeatured(service.getDefaultFeaturedIds());
} else {
if (this.getRateUpItems4().length > 0) {
info.addAllUpInfo(getRateUpItems4());
}
}
if (this.getRateUpItems5().length > 0) {
for (int id : getRateUpItems5()) {
info.addUpInfo(id);
info.addFeatured(id);
if (this.getRateUpItems5().length > 0) {
info.addAllUpInfo(getRateUpItems5());
info.addAllFeatured(getRateUpItems5());
}
}

View File

@@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import emu.lunarcore.GameConstants;
import emu.lunarcore.LunarCore;
import emu.lunarcore.data.GameData;
import emu.lunarcore.data.excel.ItemExcel;
@@ -21,21 +22,25 @@ import emu.lunarcore.server.game.BaseGameService;
import emu.lunarcore.server.game.GameServer;
import emu.lunarcore.server.packet.send.PacketDoGachaScRsp;
import emu.lunarcore.util.JsonUtils;
import emu.lunarcore.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
@Getter
public class GachaService extends BaseGameService {
private final Int2ObjectMap<GachaBanner> gachaBanners;
private GetGachaInfoScRsp cachedProto;
private int[] yellowAvatars = new int[] {1003, 1004, 1101, 1107, 1104, 1209, 1211};
private int[] yellowWeapons = new int[] {23000, 23002, 23003, 23004, 23005, 23012, 23013};
private int[] purpleAvatars = new int[] {1001, 1002, 1008, 1009, 1013, 1103, 1105, 1106, 1108, 1109, 1111, 1201, 1202, 1206, 1207};
private int[] purpleAvatars = new int[] {1001, 1002, 1008, 1009, 1013, 1103, 1105, 1106, 1108, 1109, 1110, 1111, 1201, 1202, 1206, 1207, 1210};
private int[] purpleWeapons = new int[] {21000, 21001, 21002, 21003, 21004, 21005, 21006, 21007, 21008, 21009, 21010, 21011, 21012, 21013, 21014, 21015, 21016, 21017, 21018, 21019, 21020};
private int[] blueWeapons = new int[] {20000, 20001, 20002, 20003, 20004, 20005, 20006, 20007, 20008, 20009, 20010, 20011, 20012, 20013, 20014, 20015, 20016, 20017, 20018, 20019, 20020};
private int[] defaultFeaturedIds = new int[] {23002, 1003, 1101, 1104, 23000, 23003};
private static int starglightId = 252;
private static int embersId = 251;
@@ -45,10 +50,6 @@ public class GachaService extends BaseGameService {
this.load();
}
public Int2ObjectMap<GachaBanner> getGachaBanners() {
return gachaBanners;
}
public int randomRange(int min, int max) {
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
}
@@ -69,11 +70,11 @@ public class GachaService extends BaseGameService {
}
public synchronized void doPulls(Player player, int gachaId, int times) {
// Sanity check
if (times != 10 && times != 1) {
return;
}
if (player.getInventory().getInventoryTab(ItemMainType.Equipment).getSize() + times > player.getInventory().getInventoryTab(ItemMainType.Equipment).getMaxCapacity()) {
// Sanity checks
if (times != 10 && times != 1) return;
// Prevent player from using gacha if they are at max light cones
if (player.getInventory().getTabByItemType(ItemMainType.Equipment).getSize() >= player.getInventory().getTabByItemType(ItemMainType.Equipment).getMaxCapacity()) {
player.sendPacket(new PacketDoGachaScRsp());
return;
}
@@ -87,13 +88,19 @@ public class GachaService extends BaseGameService {
// Spend currency
if (banner.getGachaType().getCostItem() > 0) {
GameItem costItem = player.getInventory().getInventoryTab(ItemMainType.Material).getItemById(banner.getGachaType().getCostItem());
GameItem costItem = player.getInventory().getMaterialByItemId(banner.getGachaType().getCostItem());
if (costItem == null || costItem.getCount() < times) {
return;
}
player.getInventory().removeItem(costItem, times);
}
// Add gacha ceiling
if (banner.getGachaType() == GachaType.Normal || banner.getGachaType() == GachaType.Newbie) {
player.getGachaInfo().addCeilingNum(times);
player.save();
}
// Roll
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner.getGachaType());
@@ -188,7 +195,7 @@ public class GachaService extends BaseGameService {
if (avatar != null) {
int dupeLevel = avatar.getRank();
int dupeItemId = avatarId + 10000; // Hacky fix so we dont have to fetch data from an excel
GameItem dupeItem = player.getInventory().getInventoryTab(ItemMainType.Material).getItemById(dupeItemId);
GameItem dupeItem = player.getInventory().getTabByItemType(ItemMainType.Material).getItemById(dupeItemId);
if (dupeItem != null) {
dupeLevel += dupeItem.getCount();
}
@@ -255,24 +262,44 @@ public class GachaService extends BaseGameService {
}
// Packets
player.sendPacket(new PacketDoGachaScRsp(banner, times, list));
player.sendPacket(new PacketDoGachaScRsp(player, banner, times, list));
}
public List<GameItem> exchangeGachaCeiling(Player player, int avatarId) {
// Sanity check
if (player.getGachaInfo().getCeilingNum() < GameConstants.GACHA_CEILING_MAX || player.getGachaInfo().isCeilingClaimed()) {
return null;
}
// Make sure the player is getting a valid avatar
if (!Utils.arrayContains(this.getYellowAvatars(), avatarId)) {
return null;
}
// Add items
List<GameItem> items = new ArrayList<>();
if (player.getAvatars().hasAvatar(avatarId)) {
// Add eidolon if player already has the avatar
items.add(new GameItem(avatarId + 10000));
} else {
items.add(new GameItem(avatarId));
}
player.getInventory().addItems(items);
player.getGachaInfo().setCeilingClaimed(true);
player.save();
return items;
}
private synchronized GetGachaInfoScRsp createProto() {
public synchronized GetGachaInfoScRsp toProto(Player player) {
var proto = GetGachaInfoScRsp.newInstance();
for (GachaBanner banner : getGachaBanners().values()) {
proto.addGachaInfoList(banner.toProto());
proto.addGachaInfoList(banner.toProto(this, player));
}
return proto;
}
public GetGachaInfoScRsp toProto() {
if (this.cachedProto == null) {
this.cachedProto = createProto();
}
return this.cachedProto;
}
}

View File

@@ -4,16 +4,19 @@ import lombok.Getter;
@Getter
public enum GachaType {
Newbie (101, 1, 2),
Normal (101, 1, 2),
AvatarUp (102, 1, 1),
WeaponUp (102, 2, 2);
Unknown (0, 0, 0, 0),
Newbie (1, 101, 1, 2),
Normal (2, 101, 1, 2),
AvatarUp (11, 102, 1, 1),
WeaponUp (12, 102, 2, 2);
private int id;
private int costItem;
private int minItemType;
private int maxItemType;
private GachaType(int costItem, int min, int max) {
private GachaType(int id, int costItem, int min, int max) {
this.id = id;
this.costItem = costItem;
this.minItemType = min;
this.maxItemType = max;

View File

@@ -1,29 +1,31 @@
package emu.lunarcore.game.gacha;
import dev.morphia.annotations.Entity;
import emu.lunarcore.GameConstants;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.proto.GachaCeilingAvatarOuterClass.GachaCeilingAvatar;
import emu.lunarcore.proto.GachaCeilingOuterClass.GachaCeiling;
import lombok.Getter;
import lombok.Setter;
@Entity(useDiscriminator = false)
@Getter @Entity(useDiscriminator = false)
public class PlayerGachaInfo {
private PlayerGachaBannerInfo standardBanner;
private PlayerGachaBannerInfo eventCharacterBanner;
private PlayerGachaBannerInfo eventWeaponBanner;
private int ceilingNum;
@Setter private boolean ceilingClaimed;
public PlayerGachaInfo() {
this.standardBanner = new PlayerGachaBannerInfo();
this.eventCharacterBanner = new PlayerGachaBannerInfo();
this.eventWeaponBanner = new PlayerGachaBannerInfo();
}
public PlayerGachaBannerInfo getStandardBanner() {
return standardBanner;
}
public PlayerGachaBannerInfo getEventCharacterBanner() {
return eventCharacterBanner;
}
public PlayerGachaBannerInfo getEventWeaponBanner() {
return eventWeaponBanner;
public void addCeilingNum(int amount) {
this.ceilingNum = Math.min(ceilingNum + amount, GameConstants.GACHA_CEILING_MAX);
}
public PlayerGachaBannerInfo getBannerInfo(GachaType type) {
@@ -35,4 +37,30 @@ public class PlayerGachaInfo {
return this.standardBanner;
}
public GachaCeiling toGachaCeiling(Player player) {
var proto = GachaCeiling.newInstance()
.setIsClaimed(this.isCeilingClaimed())
.setCeilingNum(this.getCeilingNum());
// Gacha ceiling avatars are the avatars that we can pick
var ceilingAvatars = player.getServer().getGachaService().getYellowAvatars();
for (int i = 0; i < ceilingAvatars.length; i++) {
int avatarId = ceilingAvatars[i];
int repeatedCount = 0; // Eidolon count
GameAvatar avatar = player.getAvatarById(avatarId);
if (avatar != null) {
repeatedCount = avatar.getRank();
}
var ceilingAvatar = GachaCeilingAvatar.newInstance()
.setRepeatedCnt(repeatedCount)
.setAvatarId(avatarId);
proto.addAvatarList(ceilingAvatar);
}
return proto;
}
}

View File

@@ -49,7 +49,7 @@ public class GameItem {
@Setter private boolean locked;
@Setter private int mainAffix;
private List<ItemSubAffix> subAffixes;
private List<GameItemSubAffix> subAffixes;
private int equipAvatar;
@@ -148,6 +148,14 @@ public class GameItem {
}
// Sub affixes
public void resetSubAffixes() {
if (this.subAffixes != null) {
this.subAffixes.clear();
} else {
this.subAffixes = new ArrayList<>();
}
}
public void addSubAffixes(int quantity) {
for (int i = 0; i < quantity; i++) {
@@ -180,7 +188,7 @@ public class GameItem {
}
IntSet blacklist = new IntOpenHashSet();
for (ItemSubAffix subAffix : this.getSubAffixes()) {
for (GameItemSubAffix subAffix : this.getSubAffixes()) {
blacklist.add(subAffix.getId());
}
@@ -199,12 +207,31 @@ public class GameItem {
// Add random stat
RelicSubAffixExcel subAffix = randomList.next();
this.subAffixes.add(new ItemSubAffix(subAffix));
this.subAffixes.add(new GameItemSubAffix(subAffix));
}
private void upgradeRandomSubAffix() {
ItemSubAffix subAffix = Utils.randomElement(this.subAffixes);
subAffix.incrementCount();
GameItemSubAffix subAffix = Utils.randomElement(this.subAffixes);
var subAffixExcel = GameData.getRelicSubAffixExcel(this.getExcel().getRelicExcel().getSubAffixGroup(), subAffix.getId());
subAffix.incrementCount(subAffixExcel.getStepNum());
}
/**
* Returns the current count of sub affixes this item has
*/
public int getCurrentSubAffixCount() {
if (this.subAffixes == null) return 0;
return this.subAffixes
.stream()
.reduce(0, (subtotal, subAffix) -> subtotal + subAffix.getCount(), Integer::sum);
}
/**
* Returns the maximum amount of sub affixes this item should normally have
*/
public int getMaxNormalSubAffixCount() {
return (getExcel().getRarity().getVal() - 1) + (int) Math.floor(this.getLevel() / 3.0);
}
// Database

View File

@@ -8,24 +8,29 @@ import lombok.Getter;
@Getter
@Entity(useDiscriminator = false)
public class ItemSubAffix {
public class GameItemSubAffix {
private int id; // Affix id
private int count;
private int step;
@Deprecated
public ItemSubAffix() {
public GameItemSubAffix() {
// Morphia only!
}
public ItemSubAffix(RelicSubAffixExcel subAffix) {
public GameItemSubAffix(RelicSubAffixExcel subAffix) {
this(subAffix, 1);
}
public GameItemSubAffix(RelicSubAffixExcel subAffix, int count) {
this.id = subAffix.getAffixID();
this.count = 1;
this.step = Utils.randomRange(0, subAffix.getStepNum());
this.count = count;
this.step = Utils.randomRange(0, count * subAffix.getStepNum());
}
public void incrementCount() {
public void incrementCount(int stepNum) {
this.count += 1;
this.step += Utils.randomRange(0, stepNum);
}
public RelicAffix toProto() {
@@ -36,4 +41,4 @@ public class ItemSubAffix {
return proto;
}
}
}

View File

@@ -16,63 +16,73 @@ import emu.lunarcore.game.avatar.AvatarStorage;
import emu.lunarcore.game.avatar.GameAvatar;
import emu.lunarcore.game.enums.ItemMainType;
import emu.lunarcore.game.enums.ItemSubType;
import emu.lunarcore.game.inventory.tabs.EquipInventoryTab;
import emu.lunarcore.game.inventory.tabs.InventoryTab;
import emu.lunarcore.game.inventory.tabs.InventoryTabType;
import emu.lunarcore.game.inventory.tabs.MaterialInventoryTab;
import emu.lunarcore.game.player.BasePlayerManager;
import emu.lunarcore.game.player.Player;
import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify;
import emu.lunarcore.server.packet.send.PacketScenePlaneEventScNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
public class Inventory extends BasePlayerManager {
private final Long2ObjectMap<GameItem> store;
private final Int2ObjectMap<GameItem> store;
private final Int2ObjectMap<InventoryTab> inventoryTypes;
private int nextInternalUid;
public Inventory(Player player) {
super(player);
this.store = new Long2ObjectOpenHashMap<>();
this.store = new Int2ObjectOpenHashMap<>();
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
this.createInventoryTab(ItemMainType.Equipment, new EquipInventoryTab(1500));
this.createInventoryTab(ItemMainType.Relic, new EquipInventoryTab(1500));
this.createInventoryTab(ItemMainType.Material, new MaterialInventoryTab(2000));
this.createTab(InventoryTabType.EQUIPMENT, new EquipInventoryTab(GameConstants.INVENTORY_MAX_EQUIPMENT));
this.createTab(InventoryTabType.RELIC, new EquipInventoryTab(GameConstants.INVENTORY_MAX_RELIC));
this.createTab(InventoryTabType.MATERIAL, new MaterialInventoryTab(GameConstants.INVENTORY_MAX_MATERIAL));
}
public AvatarStorage getAvatarStorage() {
return this.getPlayer().getAvatars();
}
public Long2ObjectMap<GameItem> getItems() {
public Int2ObjectMap<GameItem> getItems() {
return store;
}
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
return inventoryTypes;
}
public InventoryTab getInventoryTab(ItemMainType type) {
return getInventoryTypes().get(type.getVal());
}
public void createInventoryTab(ItemMainType type, InventoryTab tab) {
this.getInventoryTypes().put(type.getVal(), tab);
}
public int getNextItemInternalUid() {
return ++nextInternalUid;
}
/* Returns an item using its internal uid
* */
// Inventory tabs
public InventoryTab getTabByItemType(ItemMainType type) {
return getTab(type.getTabType());
}
public InventoryTab getTab(InventoryTabType type) {
if (type == null) return null;
return this.inventoryTypes.get(type.getVal());
}
public void createTab(InventoryTabType type, InventoryTab tab) {
this.inventoryTypes.put(type.getVal(), tab);
}
// Items
/**
* Returns an item using its internal uid
* @param uid
* @return
*/
public synchronized GameItem getItemByUid(int uid) {
return this.getItems().get(uid);
}
public synchronized GameItem getMaterialByItemId(int id) {
return this.getInventoryTab(ItemMainType.Material).getItemById(id);
return this.getTab(InventoryTabType.MATERIAL).getItemById(id);
}
public GameItem getItemByParam(ItemParam param) {
@@ -146,8 +156,12 @@ public class Inventory extends BasePlayerManager {
}
public List<GameItem> addItemParams(Collection<ItemParam> params) {
return addItemParams(params, 1);
}
public List<GameItem> addItemParams(Collection<ItemParam> params, int modifier) {
// TODO handle params if they are equipment or relics
List<GameItem> items = params.stream().map(param -> new GameItem(param.getId(), param.getCount())).toList();
List<GameItem> items = params.stream().map(param -> new GameItem(param.getId(), param.getCount() * modifier)).toList();
return addItems(items, false);
}
@@ -160,7 +174,7 @@ public class Inventory extends BasePlayerManager {
// Add item to inventory store
ItemMainType type = item.getExcel().getItemMainType();
ItemSubType subType = item.getExcel().getItemSubType();
InventoryTab tab = getInventoryTab(type);
InventoryTab tab = getTabByItemType(type);
// Add
switch (type) {
@@ -188,12 +202,17 @@ public class Inventory extends BasePlayerManager {
}
return null;
case Usable:
// Add head icon
if (subType == ItemSubType.HeadIcon) {
getPlayer().addHeadIcon(item.getItemId());
return item;
}
return null;
case Material:
// Skip if not food item
if (subType != ItemSubType.Food) {
return null;
}
default:
if (tab == null) {
return null;
}
@@ -216,8 +235,6 @@ public class Inventory extends BasePlayerManager {
existingItem.save();
return existingItem;
}
default:
return null;
}
}
@@ -368,7 +385,7 @@ public class Inventory extends BasePlayerManager {
// Remove from inventory tab too
InventoryTab tab = null;
if (item.getExcel() != null) {
tab = getInventoryTab(item.getExcel().getItemMainType());
tab = getTabByItemType(item.getExcel().getItemMainType());
if (tab != null) {
tab.onRemoveItem(item);
@@ -428,20 +445,37 @@ public class Inventory extends BasePlayerManager {
// Use item
public List<GameItem> useItem(int itemId, int count, int baseAvatarId) {
// Verify that the player actually has the item
GameItem useItem = this.getMaterialByItemId(itemId);
if (useItem == null || useItem.getCount() < count) {
public List<GameItem> useItem(int itemId, int count, int avatarId) {
// Sanity
if (count <= 0) {
return null;
}
// Remove item from inventory
this.removeItem(useItem, count);
// Verify that the player actually has the item
GameItem useItem = this.getMaterialByItemId(itemId);
if (useItem == null || useItem.getCount() < count || useItem.getExcel().getUseMethod() == null) {
return null;
}
// Use the item now
// Get use excel
var itemUseExcel = GameData.getItemUseExcelMap().get(useItem.getExcel().getUseDataID());
if (itemUseExcel == null) return null;
// Setup variables
boolean usedItem = false;
// Handle item useMethod
// TODO write better handler for this later
if (itemId == 201) {
this.getPlayer().addStamina(60 * count);
usedItem = switch (useItem.getExcel().getUseMethod()) {
case FixedRewardGift -> ItemUseHandler.handleFixedRewardGift(getPlayer(), itemUseExcel, avatarId, count);
case TeamSpecificFoodBenefit -> ItemUseHandler.handleTeamSpecificFoodBenefit(getPlayer(), itemUseExcel, avatarId, count);
case ExternalSystemFoodBenefit -> ItemUseHandler.handleExternalSystemFoodBenefit(getPlayer(), itemUseExcel, avatarId, count);
default -> false;
};
// Remove item from inventory if we used it
if (usedItem) {
this.removeItem(useItem, count);
}
return null;
@@ -498,7 +532,7 @@ public class Inventory extends BasePlayerManager {
item.setExcel(excel);
// Put in inventory
InventoryTab tab = getInventoryTab(item.getExcel().getItemMainType());
InventoryTab tab = getTabByItemType(item.getExcel().getItemMainType());
putItem(item, tab);
// Equip to a character if possible

Some files were not shown because too many files have changed in this diff Show More