mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-05-07 20:45:50 +08:00
Fix whitespace [skip actions]
This commit is contained in:
parent
510d564bcb
commit
ae2d1fe438
@ -6,29 +6,29 @@ import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
public final class GameConstants {
|
||||
public static String VERSION = "2.8.0";
|
||||
public static String VERSION = "2.8.0";
|
||||
|
||||
public static final int MAX_TEAMS = 4;
|
||||
public static final int MAIN_CHARACTER_MALE = 10000005;
|
||||
public static final int MAIN_CHARACTER_FEMALE = 10000007;
|
||||
public static final Position START_POSITION = new Position(2747, 194, -1719);
|
||||
public static final int MAX_TEAMS = 4;
|
||||
public static final int MAIN_CHARACTER_MALE = 10000005;
|
||||
public static final int MAIN_CHARACTER_FEMALE = 10000007;
|
||||
public static final Position START_POSITION = new Position(2747, 194, -1719);
|
||||
|
||||
public static final int MAX_FRIENDS = 45;
|
||||
public static final int MAX_FRIEND_REQUESTS = 50;
|
||||
public static final int MAX_FRIENDS = 45;
|
||||
public static final int MAX_FRIEND_REQUESTS = 50;
|
||||
|
||||
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
|
||||
public static final int SERVER_CONSOLE_UID = 99; // The UID of the server console's "player".
|
||||
|
||||
public static final int BATTLE_PASS_MAX_LEVEL = 50;
|
||||
public static final int BATTLE_PASS_POINT_PER_LEVEL = 1000;
|
||||
public static final int BATTLE_PASS_POINT_PER_WEEK = 10000;
|
||||
public static final int BATTLE_PASS_LEVEL_PRICE = 150;
|
||||
public static final int BATTLE_PASS_CURRENT_INDEX = 2;
|
||||
public static final int BATTLE_PASS_MAX_LEVEL = 50;
|
||||
public static final int BATTLE_PASS_POINT_PER_LEVEL = 1000;
|
||||
public static final int BATTLE_PASS_POINT_PER_WEEK = 10000;
|
||||
public static final int BATTLE_PASS_LEVEL_PRICE = 150;
|
||||
public static final int BATTLE_PASS_CURRENT_INDEX = 2;
|
||||
|
||||
// Default entity ability hashes.
|
||||
public static final String[] DEFAULT_ABILITY_STRINGS = {
|
||||
"Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible",
|
||||
"Avatar_Freeze_Duration_Reducer", "Avatar_Attack_ReviveEnergy", "Avatar_Component_Initializer", "Avatar_FallAnthem_Achievement_Listener"
|
||||
};
|
||||
public static final int[] DEFAULT_ABILITY_HASHES = Arrays.stream(DEFAULT_ABILITY_STRINGS).mapToInt(Utils::abilityHash).toArray();
|
||||
public static final int DEFAULT_ABILITY_NAME = Utils.abilityHash("Default");
|
||||
// Default entity ability hashes.
|
||||
public static final String[] DEFAULT_ABILITY_STRINGS = {
|
||||
"Avatar_DefaultAbility_VisionReplaceDieInvincible", "Avatar_DefaultAbility_AvartarInShaderChange", "Avatar_SprintBS_Invincible",
|
||||
"Avatar_Freeze_Duration_Reducer", "Avatar_Attack_ReviveEnergy", "Avatar_Component_Initializer", "Avatar_FallAnthem_Achievement_Listener"
|
||||
};
|
||||
public static final int[] DEFAULT_ABILITY_HASHES = Arrays.stream(DEFAULT_ABILITY_STRINGS).mapToInt(Utils::abilityHash).toArray();
|
||||
public static final int DEFAULT_ABILITY_NAME = Utils.abilityHash("Default");
|
||||
}
|
||||
|
@ -217,7 +217,7 @@ public final class Grasscutter {
|
||||
*/
|
||||
private static void onShutdown() {
|
||||
// Disable all plugins.
|
||||
if(pluginManager != null)
|
||||
if (pluginManager != null)
|
||||
pluginManager.disablePlugins();
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ public final class DefaultAuthentication implements AuthenticationSystem {
|
||||
private OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication();
|
||||
|
||||
public DefaultAuthentication() {
|
||||
if(ACCOUNT.EXPERIMENTAL_RealPassword) {
|
||||
if (ACCOUNT.EXPERIMENTAL_RealPassword) {
|
||||
passwordAuthenticator = new ExperimentalPasswordAuthenticator();
|
||||
} else {
|
||||
passwordAuthenticator = new PasswordAuthenticator();
|
||||
|
@ -167,7 +167,7 @@ public final class CommandMap {
|
||||
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Sets default targetPlayer to the UID provided.
|
||||
try {
|
||||
int uid = Integer.parseInt(targetUid);
|
||||
@ -237,7 +237,7 @@ public final class CommandMap {
|
||||
Command annotation = this.annotations.get(label);
|
||||
|
||||
// Resolve targetPlayer
|
||||
try{
|
||||
try {
|
||||
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return;
|
||||
|
@ -39,8 +39,8 @@ public final class AccountCommand implements CommandHandler {
|
||||
int uid = 0;
|
||||
String password = "";
|
||||
|
||||
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
if(args.size() < 3) {
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires a password argument");
|
||||
CommandHandler.sendMessage(null, "Usage: account create <username> <password> [uid]");
|
||||
|
||||
@ -53,7 +53,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
uid = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid"));
|
||||
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid");
|
||||
CommandHandler.sendMessage(null, "Usage: account create <username> <password> [uid]");
|
||||
}
|
||||
@ -76,7 +76,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(null, translate(sender, "commands.account.exists"));
|
||||
return;
|
||||
} else {
|
||||
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) {
|
||||
account.setPassword(BCrypt.withDefaults().hashToString(12, password.toCharArray()));
|
||||
}
|
||||
account.addPermission("*");
|
||||
@ -93,7 +93,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(null, translate(sender, "commands.account.no_account"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get the player for the account.
|
||||
// If that player is currently online, we kick them before proceeding with the deletion.
|
||||
Player player = Grasscutter.getGameServer().getPlayerByAccountId(toDelete.getId());
|
||||
@ -106,12 +106,12 @@ public final class AccountCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(null, translate(sender, "commands.account.delete"));
|
||||
return;
|
||||
case "resetpass":
|
||||
if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) {
|
||||
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) {
|
||||
CommandHandler.sendMessage(null, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(args.size() != 3) {
|
||||
if (args.size() != 3) {
|
||||
CommandHandler.sendMessage(null, "Invalid Args");
|
||||
CommandHandler.sendMessage(null, "Usage: account resetpass <username> <password>");
|
||||
return;
|
||||
|
@ -28,7 +28,7 @@ public final class AnnounceCommand implements CommandHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)){
|
||||
switch (args.get(0)) {
|
||||
case "tpl":
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.announce.command_usage");
|
||||
@ -37,7 +37,7 @@ public final class AnnounceCommand implements CommandHandler {
|
||||
|
||||
var templateId = Integer.parseInt(args.get(1));
|
||||
var tpl = manager.getAnnounceConfigItemMap().get(templateId);
|
||||
if(tpl == null){
|
||||
if (tpl == null) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.not_found", templateId));
|
||||
return;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public final class CoopCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// There's no target==host check but this just places them in multiplayer in their own world which seems fine.
|
||||
if (targetPlayer.isInMultiplayer()) {
|
||||
targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer);
|
||||
|
@ -21,10 +21,10 @@ public final class EnterDungeonCommand implements CommandHandler {
|
||||
try {
|
||||
int dungeonId = Integer.parseInt(args.get(0));
|
||||
if (dungeonId == targetPlayer.getSceneId()) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
|
||||
return;
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
boolean result = targetPlayer.getServer().getDungeonSystem().enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
|
||||
|
||||
if (!result) {
|
||||
|
@ -257,7 +257,7 @@ public final class GiveCommand implements CommandHandler {
|
||||
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
|
||||
}
|
||||
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
else if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
|
||||
}
|
||||
|
||||
@ -352,11 +352,11 @@ public final class GiveCommand implements CommandHandler {
|
||||
return affixes;
|
||||
}
|
||||
|
||||
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException {
|
||||
// If the given substat text is an integer, we just use that as the append prop ID.
|
||||
try {
|
||||
return Integer.parseInt(substatText);
|
||||
} catch (NumberFormatException ignored) {
|
||||
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException {
|
||||
// If the given substat text is an integer, we just use that as the append prop ID.
|
||||
try {
|
||||
return Integer.parseInt(substatText);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// If the argument was not an integer, we try to determine
|
||||
// the append prop ID from the given text + artifact information.
|
||||
// A substat string has the format `substat_tier`, with the
|
||||
@ -378,8 +378,8 @@ public final class GiveCommand implements CommandHandler {
|
||||
substatTier -= 1; // 1-indexed to 0-indexed
|
||||
substatTier = Math.min(Math.max(0, substatTier), substats.size() - 1);
|
||||
return substats.get(substatTier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseRelicArgs(GiveItemParameters param, List<String> args) throws IllegalArgumentException {
|
||||
// Get the main stat from the arguments.
|
||||
@ -476,11 +476,11 @@ public final class GiveCommand implements CommandHandler {
|
||||
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
|
||||
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
|
||||
""");
|
||||
|
||||
|
||||
private static final SparseSet illegalRelicIds = new SparseSet("""
|
||||
20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
|
||||
""");
|
||||
|
||||
|
||||
private static final SparseSet illegalItemIds = new SparseSet("""
|
||||
100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
|
||||
105001, 105004, 106000-107000, 107011, 108000, 109000-110000,
|
||||
|
@ -22,13 +22,13 @@ public final class HelpCommand implements CommandHandler {
|
||||
}
|
||||
}
|
||||
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
|
||||
if(annotation.permission().isEmpty() || annotation.permission().isBlank()) {
|
||||
if (annotation.permission().isEmpty() || annotation.permission().isBlank()) {
|
||||
builder.append(translate(player, "commands.help.tip_need_no_permission"));
|
||||
} else {
|
||||
builder.append(annotation.permission());
|
||||
}
|
||||
|
||||
if(!annotation.permissionTargeted().isEmpty() && !annotation.permissionTargeted().isBlank()) {
|
||||
if (!annotation.permissionTargeted().isEmpty() && !annotation.permissionTargeted().isBlank()) {
|
||||
String permissionTargeted = annotation.permissionTargeted();
|
||||
builder.append(" ").append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
|
||||
}
|
||||
|
@ -15,13 +15,13 @@ public final class ReloadCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_start"));
|
||||
|
||||
|
||||
Grasscutter.loadConfig();
|
||||
Grasscutter.loadLanguage();
|
||||
Grasscutter.getGameServer().getGachaSystem().load();
|
||||
Grasscutter.getGameServer().getDropSystem().load();
|
||||
Grasscutter.getGameServer().getShopSystem().load();
|
||||
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done"));
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ public final class SendMailCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int senderId;
|
||||
if(sender != null) {
|
||||
if (sender != null) {
|
||||
senderId = sender.getUid();
|
||||
} else {
|
||||
senderId = -1;
|
||||
@ -162,7 +162,7 @@ public final class SendMailCommand implements CommandHandler {
|
||||
}
|
||||
|
||||
private String getConstructionArgs(int stage, Player sender) {
|
||||
return switch(stage) {
|
||||
return switch (stage) {
|
||||
case 0 -> translate(sender, "commands.sendMail.title");
|
||||
case 1 -> translate(sender, "commands.sendMail.message");
|
||||
case 2 -> translate(sender, "commands.sendMail.sender");
|
||||
|
@ -1,205 +1,205 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.tower.TowerLevelRecord;
|
||||
|
||||
@Command(label = "setprop", usage = "setprop|prop <prop> <value>", aliases = {"prop"}, permission = "player.setprop", permissionTargeted = "player.setprop.others", description = "commands.setProp.description")
|
||||
public final class SetPropCommand implements CommandHandler {
|
||||
static enum PseudoProp {
|
||||
NONE,
|
||||
WORLD_LEVEL,
|
||||
TOWER_LEVEL,
|
||||
BP_LEVEL,
|
||||
GOD_MODE,
|
||||
NO_STAMINA,
|
||||
UNLIMITED_ENERGY
|
||||
}
|
||||
|
||||
static class Prop {
|
||||
String name;
|
||||
PlayerProperty prop;
|
||||
PseudoProp pseudoProp;
|
||||
|
||||
public Prop(PlayerProperty prop) {
|
||||
this(prop.toString(), prop, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name) {
|
||||
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name, PseudoProp pseudoProp) {
|
||||
this(name, PlayerProperty.PROP_NONE, pseudoProp);
|
||||
}
|
||||
|
||||
public Prop(String name, PlayerProperty prop) {
|
||||
this(name, prop, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
|
||||
this.name = name;
|
||||
this.prop = prop;
|
||||
this.pseudoProp = pseudoProp;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Prop> props;
|
||||
|
||||
public SetPropCommand() {
|
||||
this.props = new HashMap<>();
|
||||
// Full PlayerProperty enum that won't be advertised but can be used by devs
|
||||
for (PlayerProperty prop : PlayerProperty.values()) {
|
||||
String name = prop.toString().substring(5); // PROP_EXP -> EXP
|
||||
String key = name.toLowerCase(); // EXP -> exp
|
||||
this.props.put(key, new Prop(name, prop));
|
||||
}
|
||||
// Add special props
|
||||
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
|
||||
this.props.put("worldlevel", worldlevel);
|
||||
this.props.put("wl", worldlevel);
|
||||
|
||||
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
|
||||
this.props.put("abyss", abyss);
|
||||
this.props.put("abyssfloor", abyss);
|
||||
this.props.put("ut", abyss);
|
||||
this.props.put("tower", abyss);
|
||||
this.props.put("towerlevel", abyss);
|
||||
this.props.put("unlocktower", abyss);
|
||||
|
||||
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
|
||||
this.props.put("bplevel", bplevel);
|
||||
this.props.put("bp", bplevel);
|
||||
this.props.put("battlepass", bplevel);
|
||||
|
||||
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
|
||||
this.props.put("godmode", godmode);
|
||||
this.props.put("god", godmode);
|
||||
|
||||
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
|
||||
this.props.put("nostamina", nostamina);
|
||||
this.props.put("nostam", nostamina);
|
||||
this.props.put("ns", nostamina);
|
||||
|
||||
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
|
||||
this.props.put("unlimitedenergy", unlimitedenergy);
|
||||
this.props.put("ue", unlimitedenergy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
|
||||
return;
|
||||
}
|
||||
String propStr = args.get(0).toLowerCase();
|
||||
String valueStr = args.get(1).toLowerCase();
|
||||
int value;
|
||||
|
||||
if (!props.containsKey(propStr)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
value = switch(valueStr.toLowerCase()) {
|
||||
case "on", "true" -> 1;
|
||||
case "off", "false" -> 0;
|
||||
case "toggle" -> -1;
|
||||
default -> Integer.parseInt(valueStr);
|
||||
};
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
Prop prop = props.get(propStr);
|
||||
|
||||
success = switch (prop.pseudoProp) {
|
||||
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
|
||||
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
|
||||
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
|
||||
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
|
||||
default -> targetPlayer.setProperty(prop.prop, value);
|
||||
};
|
||||
|
||||
if (success) {
|
||||
if (targetPlayer == sender) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
|
||||
} else {
|
||||
String uidStr = targetPlayer.getAccount().getId();
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
|
||||
}
|
||||
} else {
|
||||
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
|
||||
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
|
||||
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
|
||||
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
|
||||
if (topFloor < 0 || topFloor > floorIds.size()) {
|
||||
String min = Integer.toString(0);
|
||||
String max = Integer.toString(floorIds.size());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
|
||||
// Add records for each unlocked floor
|
||||
for (int floor : floorIds.subList(0, topFloor)) {
|
||||
if (!recordMap.containsKey(floor)) {
|
||||
recordMap.put(floor, new TowerLevelRecord(floor));
|
||||
}
|
||||
}
|
||||
// Remove records for each floor past our target
|
||||
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
|
||||
if (recordMap.containsKey(floor)) {
|
||||
recordMap.remove(floor);
|
||||
}
|
||||
}
|
||||
// Six stars required on Floor 8 to unlock Floor 9+
|
||||
if (topFloor > 8) {
|
||||
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
|
||||
boolean enabled = switch (pseudoProp) {
|
||||
case GOD_MODE -> targetPlayer.inGodmode();
|
||||
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
|
||||
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
|
||||
default -> false;
|
||||
};
|
||||
enabled = switch (value) {
|
||||
case -1 -> !enabled;
|
||||
case 0 -> false;
|
||||
default -> true;
|
||||
};
|
||||
|
||||
switch (pseudoProp) {
|
||||
case GOD_MODE:
|
||||
targetPlayer.setGodmode(enabled);
|
||||
break;
|
||||
case NO_STAMINA:
|
||||
targetPlayer.setUnlimitedStamina(enabled);
|
||||
break;
|
||||
case UNLIMITED_ENERGY:
|
||||
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.tower.TowerLevelRecord;
|
||||
|
||||
@Command(label = "setprop", usage = "setprop|prop <prop> <value>", aliases = {"prop"}, permission = "player.setprop", permissionTargeted = "player.setprop.others", description = "commands.setProp.description")
|
||||
public final class SetPropCommand implements CommandHandler {
|
||||
static enum PseudoProp {
|
||||
NONE,
|
||||
WORLD_LEVEL,
|
||||
TOWER_LEVEL,
|
||||
BP_LEVEL,
|
||||
GOD_MODE,
|
||||
NO_STAMINA,
|
||||
UNLIMITED_ENERGY
|
||||
}
|
||||
|
||||
static class Prop {
|
||||
String name;
|
||||
PlayerProperty prop;
|
||||
PseudoProp pseudoProp;
|
||||
|
||||
public Prop(PlayerProperty prop) {
|
||||
this(prop.toString(), prop, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name) {
|
||||
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name, PseudoProp pseudoProp) {
|
||||
this(name, PlayerProperty.PROP_NONE, pseudoProp);
|
||||
}
|
||||
|
||||
public Prop(String name, PlayerProperty prop) {
|
||||
this(name, prop, PseudoProp.NONE);
|
||||
}
|
||||
|
||||
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
|
||||
this.name = name;
|
||||
this.prop = prop;
|
||||
this.pseudoProp = pseudoProp;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Prop> props;
|
||||
|
||||
public SetPropCommand() {
|
||||
this.props = new HashMap<>();
|
||||
// Full PlayerProperty enum that won't be advertised but can be used by devs
|
||||
for (PlayerProperty prop : PlayerProperty.values()) {
|
||||
String name = prop.toString().substring(5); // PROP_EXP -> EXP
|
||||
String key = name.toLowerCase(); // EXP -> exp
|
||||
this.props.put(key, new Prop(name, prop));
|
||||
}
|
||||
// Add special props
|
||||
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
|
||||
this.props.put("worldlevel", worldlevel);
|
||||
this.props.put("wl", worldlevel);
|
||||
|
||||
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
|
||||
this.props.put("abyss", abyss);
|
||||
this.props.put("abyssfloor", abyss);
|
||||
this.props.put("ut", abyss);
|
||||
this.props.put("tower", abyss);
|
||||
this.props.put("towerlevel", abyss);
|
||||
this.props.put("unlocktower", abyss);
|
||||
|
||||
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
|
||||
this.props.put("bplevel", bplevel);
|
||||
this.props.put("bp", bplevel);
|
||||
this.props.put("battlepass", bplevel);
|
||||
|
||||
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
|
||||
this.props.put("godmode", godmode);
|
||||
this.props.put("god", godmode);
|
||||
|
||||
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
|
||||
this.props.put("nostamina", nostamina);
|
||||
this.props.put("nostam", nostamina);
|
||||
this.props.put("ns", nostamina);
|
||||
|
||||
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
|
||||
this.props.put("unlimitedenergy", unlimitedenergy);
|
||||
this.props.put("ue", unlimitedenergy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() != 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
|
||||
return;
|
||||
}
|
||||
String propStr = args.get(0).toLowerCase();
|
||||
String valueStr = args.get(1).toLowerCase();
|
||||
int value;
|
||||
|
||||
if (!props.containsKey(propStr)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
value = switch (valueStr.toLowerCase()) {
|
||||
case "on", "true" -> 1;
|
||||
case "off", "false" -> 0;
|
||||
case "toggle" -> -1;
|
||||
default -> Integer.parseInt(valueStr);
|
||||
};
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
Prop prop = props.get(propStr);
|
||||
|
||||
success = switch (prop.pseudoProp) {
|
||||
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
|
||||
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
|
||||
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
|
||||
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
|
||||
default -> targetPlayer.setProperty(prop.prop, value);
|
||||
};
|
||||
|
||||
if (success) {
|
||||
if (targetPlayer == sender) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
|
||||
} else {
|
||||
String uidStr = targetPlayer.getAccount().getId();
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
|
||||
}
|
||||
} else {
|
||||
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
|
||||
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
|
||||
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
|
||||
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
|
||||
if (topFloor < 0 || topFloor > floorIds.size()) {
|
||||
String min = Integer.toString(0);
|
||||
String max = Integer.toString(floorIds.size());
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
|
||||
// Add records for each unlocked floor
|
||||
for (int floor : floorIds.subList(0, topFloor)) {
|
||||
if (!recordMap.containsKey(floor)) {
|
||||
recordMap.put(floor, new TowerLevelRecord(floor));
|
||||
}
|
||||
}
|
||||
// Remove records for each floor past our target
|
||||
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
|
||||
if (recordMap.containsKey(floor)) {
|
||||
recordMap.remove(floor);
|
||||
}
|
||||
}
|
||||
// Six stars required on Floor 8 to unlock Floor 9+
|
||||
if (topFloor > 8) {
|
||||
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
|
||||
boolean enabled = switch (pseudoProp) {
|
||||
case GOD_MODE -> targetPlayer.inGodmode();
|
||||
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
|
||||
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
|
||||
default -> false;
|
||||
};
|
||||
enabled = switch (value) {
|
||||
case -1 -> !enabled;
|
||||
case 0 -> false;
|
||||
default -> true;
|
||||
};
|
||||
|
||||
switch (pseudoProp) {
|
||||
case GOD_MODE:
|
||||
targetPlayer.setGodmode(enabled);
|
||||
break;
|
||||
case NO_STAMINA:
|
||||
targetPlayer.setUnlimitedStamina(enabled);
|
||||
break;
|
||||
case UNLIMITED_ENERGY:
|
||||
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ public final class SpawnCommand implements CommandHandler {
|
||||
int id = 0; // This is just to shut up the linter, it's not a real default
|
||||
int amount = 1;
|
||||
int level = 1;
|
||||
float x = 0, y = 0, z = 0;
|
||||
float x = 0, y = 0, z = 0;
|
||||
switch (args.size()) {
|
||||
case 6:
|
||||
try {
|
||||
@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MonsterData monsterData = GameData.getMonsterDataMap().get(id);
|
||||
GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
|
||||
ItemData itemData = GameData.getItemDataMap().get(id);
|
||||
@ -72,21 +72,21 @@ public final class SpawnCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Scene scene = targetPlayer.getScene();
|
||||
|
||||
|
||||
if (scene.getEntities().size() + amount > GAME_OPTIONS.sceneEntityLimit) {
|
||||
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double maxRadius = Math.sqrt(amount * 0.2 / Math.PI);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = GetRandomPositionInCircle(targetPlayer.getPosition(), maxRadius).addY(3);
|
||||
if(x != 0 && y != 0 && z != 0) {
|
||||
if (x != 0 && y != 0 && z != 0) {
|
||||
pos = GetRandomPositionInCircle(new Position(x, y, z), maxRadius).addY(3);
|
||||
}
|
||||
GameEntity entity = null;
|
||||
@ -120,7 +120,7 @@ public final class SpawnCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", Integer.toString(amount), Integer.toString(id)));
|
||||
}
|
||||
|
||||
private Position GetRandomPositionInCircle(Position origin, double radius){
|
||||
private Position GetRandomPositionInCircle(Position origin, double radius) {
|
||||
Position target = origin.clone();
|
||||
double angle = Math.random() * 360;
|
||||
double r = Math.sqrt(Math.random() * radius * radius);
|
||||
|
@ -1,255 +1,255 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
|
||||
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
|
||||
public final class TeamCommand implements CommandHandler {
|
||||
private static final int BASE_AVATARID = 10000000;
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "add":
|
||||
if (!addCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
if (!removeCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "set":
|
||||
if (!setCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
default:
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlayer.getTeamManager().updateTeamEntities(
|
||||
new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
|
||||
}
|
||||
|
||||
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
if (args.size() > 2) {
|
||||
try {
|
||||
index = Integer.parseInt(args.get(2)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var avatarIds = args.get(1).split(",");
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var avatarId: avatarIds) {
|
||||
int id = Integer.parseInt(avatarId);
|
||||
var success = addAvatar(sender, targetPlayer, id, index);
|
||||
if (index > 0) ++index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
var avatarCount = currentTeamAvatars.size();
|
||||
|
||||
var metaIndexList = args.get(1).split(",");
|
||||
var indexes = new HashSet<Integer>();
|
||||
var ignoreList = new ArrayList<Integer>();
|
||||
for (var metaIndex: metaIndexList) {
|
||||
// step 1: parse metaIndex to indexes
|
||||
var subIndexes = transformToIndexes(metaIndex, avatarCount);
|
||||
if (subIndexes == null) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// step 2: get all of the avatar id through indexes
|
||||
for (var avatarIndex: subIndexes) {
|
||||
try {
|
||||
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
|
||||
} catch (Exception e) {
|
||||
ignoreList.add(avatarIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 3: check if user remove all of the avatar
|
||||
if (indexes.size() >= avatarCount) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
|
||||
return false;
|
||||
}
|
||||
|
||||
// step 4: hint user for ignore index
|
||||
if (!ignoreList.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
|
||||
}
|
||||
|
||||
// step 5: remove
|
||||
currentTeamAvatars.removeAll(indexes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(args.get(1)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch(Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + 1 > currentTeamAvatars.size()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
|
||||
return false;
|
||||
}
|
||||
|
||||
int avatarId;
|
||||
try {
|
||||
avatarId = Integer.parseInt(args.get(2));
|
||||
} catch(Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2));
|
||||
return false;
|
||||
}
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTeamAvatars.set(index, avatarId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (index < 0) {
|
||||
currentTeamAvatars.add(avatarId);
|
||||
} else {
|
||||
currentTeamAvatars.add(index, avatarId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
|
||||
// step 1: check if metaIndexes is a special constants
|
||||
if (metaIndexes.equals("first")) {
|
||||
return List.of(1);
|
||||
} else if (metaIndexes.equals("last")) {
|
||||
return List.of(listLength);
|
||||
}
|
||||
|
||||
// step 2: check if metaIndexes is a range
|
||||
if (metaIndexes.contains("-")) {
|
||||
var range = metaIndexes.split("-");
|
||||
if (range.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int min, max;
|
||||
try {
|
||||
min = switch (range[0]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[0]);
|
||||
};
|
||||
|
||||
max = switch (range[1]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[1]);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
var indexes = new ArrayList<Integer>();
|
||||
for (int i = min; i <= max; ++i) {
|
||||
indexes.add(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
// step 3: index is a value, simply return
|
||||
try {
|
||||
int index = Integer.parseInt(metaIndexes);
|
||||
return List.of(index);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
|
||||
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
|
||||
public final class TeamCommand implements CommandHandler {
|
||||
private static final int BASE_AVATARID = 10000000;
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "add":
|
||||
if (!addCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
if (!removeCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
case "set":
|
||||
if (!setCommand(sender, targetPlayer, args)) return;
|
||||
break;
|
||||
|
||||
default:
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlayer.getTeamManager().updateTeamEntities(
|
||||
new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
|
||||
}
|
||||
|
||||
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = -1;
|
||||
if (args.size() > 2) {
|
||||
try {
|
||||
index = Integer.parseInt(args.get(2)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var avatarIds = args.get(1).split(",");
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var avatarId: avatarIds) {
|
||||
int id = Integer.parseInt(avatarId);
|
||||
var success = addAvatar(sender, targetPlayer, id, index);
|
||||
if (index > 0) ++index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
var avatarCount = currentTeamAvatars.size();
|
||||
|
||||
var metaIndexList = args.get(1).split(",");
|
||||
var indexes = new HashSet<Integer>();
|
||||
var ignoreList = new ArrayList<Integer>();
|
||||
for (var metaIndex: metaIndexList) {
|
||||
// step 1: parse metaIndex to indexes
|
||||
var subIndexes = transformToIndexes(metaIndex, avatarCount);
|
||||
if (subIndexes == null) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex);
|
||||
continue;
|
||||
}
|
||||
|
||||
// step 2: get all of the avatar id through indexes
|
||||
for (var avatarIndex: subIndexes) {
|
||||
try {
|
||||
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
|
||||
} catch (Exception e) {
|
||||
ignoreList.add(avatarIndex);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 3: check if user remove all of the avatar
|
||||
if (indexes.size() >= avatarCount) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
|
||||
return false;
|
||||
}
|
||||
|
||||
// step 4: hint user for ignore index
|
||||
if (!ignoreList.isEmpty()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
|
||||
}
|
||||
|
||||
// step 5: remove
|
||||
currentTeamAvatars.removeAll(indexes);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
|
||||
return false;
|
||||
}
|
||||
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
|
||||
int index;
|
||||
try {
|
||||
index = Integer.parseInt(args.get(1)) - 1;
|
||||
if (index < 0) index = 0;
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index + 1 > currentTeamAvatars.size()) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
|
||||
return false;
|
||||
}
|
||||
|
||||
int avatarId;
|
||||
try {
|
||||
avatarId = Integer.parseInt(args.get(2));
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2));
|
||||
return false;
|
||||
}
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTeamAvatars.set(index, avatarId);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
|
||||
if (avatarId < BASE_AVATARID) {
|
||||
avatarId += BASE_AVATARID;
|
||||
}
|
||||
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
|
||||
if (currentTeamAvatars.contains(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
|
||||
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
|
||||
return false;
|
||||
}
|
||||
if (index < 0) {
|
||||
currentTeamAvatars.add(avatarId);
|
||||
} else {
|
||||
currentTeamAvatars.add(index, avatarId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
|
||||
// step 1: check if metaIndexes is a special constants
|
||||
if (metaIndexes.equals("first")) {
|
||||
return List.of(1);
|
||||
} else if (metaIndexes.equals("last")) {
|
||||
return List.of(listLength);
|
||||
}
|
||||
|
||||
// step 2: check if metaIndexes is a range
|
||||
if (metaIndexes.contains("-")) {
|
||||
var range = metaIndexes.split("-");
|
||||
if (range.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int min, max;
|
||||
try {
|
||||
min = switch (range[0]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[0]);
|
||||
};
|
||||
|
||||
max = switch (range[1]) {
|
||||
case "first" -> 1;
|
||||
case "last" -> listLength;
|
||||
default -> Integer.parseInt(range[1]);
|
||||
};
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (min > max) {
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
var indexes = new ArrayList<Integer>();
|
||||
for (int i = min; i <= max; ++i) {
|
||||
indexes.add(i);
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
|
||||
// step 3: index is a value, simply return
|
||||
try {
|
||||
int index = Integer.parseInt(metaIndexes);
|
||||
return List.of(index);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public final class TeleportAllCommand implements CommandHandler {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (Player player : targetPlayer.getWorld().getPlayers()) {
|
||||
if (player.equals(targetPlayer))
|
||||
continue;
|
||||
@ -27,7 +27,7 @@ public final class TeleportAllCommand implements CommandHandler {
|
||||
|
||||
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos);
|
||||
}
|
||||
|
||||
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success"));
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ public final class TeleportCommand implements CommandHandler {
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success",
|
||||
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
|
||||
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success",
|
||||
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
|
||||
Float.toString(z), Integer.toString(sceneId))
|
||||
);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public class ConfigContainer {
|
||||
try { // Check if the server is using a legacy config.
|
||||
JsonObject configObject = Grasscutter.getGsonFactory()
|
||||
.fromJson(new FileReader(Grasscutter.configFile), JsonObject.class);
|
||||
if(!configObject.has("version")) {
|
||||
if (!configObject.has("version")) {
|
||||
Grasscutter.getLogger().info("Updating legacy ..");
|
||||
Grasscutter.saveConfig(null);
|
||||
}
|
||||
@ -37,7 +37,7 @@ public class ConfigContainer {
|
||||
var existing = config.version;
|
||||
var latest = version();
|
||||
|
||||
if(existing == latest)
|
||||
if (existing == latest)
|
||||
return;
|
||||
|
||||
// Create a new configuration instance.
|
||||
@ -135,20 +135,20 @@ public class ConfigContainer {
|
||||
public static class Game {
|
||||
public String bindAddress = "0.0.0.0";
|
||||
public int bindPort = 22102;
|
||||
|
||||
|
||||
/* This is the address used in the default region. */
|
||||
public String accessAddress = "127.0.0.1";
|
||||
/* This is the port used in the default region. */
|
||||
public int accessPort = 0;
|
||||
|
||||
|
||||
/* Entities within a certain range will be loaded for the player */
|
||||
public int loadEntitiesForPlayerRange = 100;
|
||||
public boolean enableScriptInBigWorld = false;
|
||||
public boolean enableConsole = true;
|
||||
|
||||
|
||||
/* Controls whether packets should be logged in console or not */
|
||||
public ServerDebugMode logPackets = ServerDebugMode.NONE;
|
||||
|
||||
|
||||
public GameOptions gameOptions = new GameOptions();
|
||||
public JoinOptions joinOptions = new JoinOptions();
|
||||
public ConsoleAccount serverAccount = new ConsoleAccount();
|
||||
@ -160,7 +160,7 @@ public class ConfigContainer {
|
||||
public Region[] regions = {};
|
||||
|
||||
public String defaultName = "Grasscutter";
|
||||
|
||||
|
||||
public ServerDebugMode logRequests = ServerDebugMode.NONE;
|
||||
}
|
||||
|
||||
@ -276,4 +276,4 @@ public class ConfigContainer {
|
||||
public String Ip = "127.0.0.1";
|
||||
public int Port = 22102;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,19 +9,19 @@ import static emu.grasscutter.Grasscutter.config;
|
||||
|
||||
/**
|
||||
* A data container for the server's configuration.
|
||||
*
|
||||
*
|
||||
* Use `import static emu.grasscutter.Configuration.*;`
|
||||
* to import all configuration constants.
|
||||
*/
|
||||
public final class Configuration extends ConfigContainer {
|
||||
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
|
||||
// 'c' is short for 'config' and makes code look 'cleaner'.
|
||||
public static final ConfigContainer c = config;
|
||||
|
||||
|
||||
public static final Locale LANGUAGE = config.language.language;
|
||||
public static final Locale FALLBACK_LANGUAGE = config.language.fallback;
|
||||
public static final String DOCUMENT_LANGUAGE = config.language.document;
|
||||
@ -30,22 +30,22 @@ public final class Configuration extends ConfigContainer {
|
||||
private static final String PLUGINS_FOLDER = config.folderStructure.plugins;
|
||||
private static final String SCRIPTS_FOLDER = config.folderStructure.scripts;
|
||||
private static final String PACKETS_FOLDER = config.folderStructure.packets;
|
||||
|
||||
|
||||
public static final Server SERVER = config.server;
|
||||
public static final Database DATABASE = config.databaseInfo;
|
||||
public static final Account ACCOUNT = config.account;
|
||||
|
||||
|
||||
public static final HTTP HTTP_INFO = config.server.http;
|
||||
public static final Game GAME_INFO = config.server.game;
|
||||
public static final Dispatch DISPATCH_INFO = config.server.dispatch;
|
||||
|
||||
|
||||
public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption;
|
||||
public static final Policies HTTP_POLICIES = config.server.http.policies;
|
||||
public static final Files HTTP_STATIC_FILES = config.server.http.files;
|
||||
|
||||
|
||||
public static final GameOptions GAME_OPTIONS = config.server.game.gameOptions;
|
||||
public static final GameOptions.InventoryLimits INVENTORY_LIMITS = config.server.game.gameOptions.inventoryLimits;
|
||||
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
@ -56,11 +56,11 @@ public final class Configuration extends ConfigContainer {
|
||||
public static String DATA(String path) {
|
||||
return Paths.get(DATA_FOLDER, path).toString();
|
||||
}
|
||||
|
||||
|
||||
public static String RESOURCE(String path) {
|
||||
return Paths.get(RESOURCES_FOLDER, path).toString();
|
||||
}
|
||||
|
||||
|
||||
public static String PLUGIN() {
|
||||
return PLUGINS_FOLDER;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class DataLoader {
|
||||
public static InputStream load(String resourcePath) throws FileNotFoundException {
|
||||
return load(resourcePath, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an input stream reader for a data file. If the file isn't found within the /data directory then it will fallback to the default within the jar resources
|
||||
*
|
||||
|
@ -22,47 +22,47 @@ public class GameDepot {
|
||||
private static Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
|
||||
private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
|
||||
private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists = new HashMap<>();
|
||||
|
||||
public static void load() {
|
||||
for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
List<ReliquaryMainPropData> list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new ArrayList<>());
|
||||
list.add(data);
|
||||
WeightedList<ReliquaryMainPropData> weightedList = relicRandomMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new WeightedList<>());
|
||||
weightedList.add(data.getWeight(), data);
|
||||
}
|
||||
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
List<ReliquaryAffixData> list = relicAffixDepot.computeIfAbsent(data.getDepotId(), k -> new ArrayList<>());
|
||||
list.add(data);
|
||||
}
|
||||
// Let the server owner know if theyre missing weights
|
||||
if (relicMainPropDepot.size() == 0 || relicAffixDepot.size() == 0) {
|
||||
Grasscutter.getLogger().error("Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder.");
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
List<ReliquaryAffixData> list = relicAffixDepot.computeIfAbsent(data.getDepotId(), k -> new ArrayList<>());
|
||||
list.add(data);
|
||||
}
|
||||
// Let the server owner know if theyre missing weights
|
||||
if (relicMainPropDepot.size() == 0 || relicAffixDepot.size() == 0) {
|
||||
Grasscutter.getLogger().error("Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder.");
|
||||
}
|
||||
}
|
||||
|
||||
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
|
||||
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
|
||||
WeightedList<ReliquaryMainPropData> depotList = relicRandomMainPropDepot.get(depot);
|
||||
if (depotList == null) {
|
||||
return null;
|
||||
}
|
||||
return depotList.next();
|
||||
}
|
||||
if (depotList == null) {
|
||||
return null;
|
||||
}
|
||||
return depotList.next();
|
||||
}
|
||||
|
||||
public static List<ReliquaryMainPropData> getRelicMainPropList(int depot) {
|
||||
return relicMainPropDepot.get(depot);
|
||||
}
|
||||
|
||||
public static List<ReliquaryAffixData> getRelicAffixList(int depot) {
|
||||
return relicAffixDepot.get(depot);
|
||||
}
|
||||
return relicAffixDepot.get(depot);
|
||||
}
|
||||
|
||||
public static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> getSpawnLists() {
|
||||
return spawnLists;
|
||||
@ -72,9 +72,9 @@ public class GameDepot {
|
||||
spawnLists.putAll(data);
|
||||
}
|
||||
|
||||
public static void setPlayerAbilities(Map<String, AvatarConfig> playerAbilities) {
|
||||
GameDepot.playerAbilities = playerAbilities;
|
||||
}
|
||||
public static void setPlayerAbilities(Map<String, AvatarConfig> playerAbilities) {
|
||||
GameDepot.playerAbilities = playerAbilities;
|
||||
}
|
||||
|
||||
public static Map<String, AvatarConfig> getPlayerAbilities() {
|
||||
return playerAbilities;
|
||||
|
@ -34,281 +34,281 @@ import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
public class ResourceLoader {
|
||||
|
||||
private static final List<String> loadedResources = new ArrayList<>();
|
||||
private static final List<String> loadedResources = new ArrayList<>();
|
||||
|
||||
public static List<Class<?>> getResourceDefClasses() {
|
||||
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
||||
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
|
||||
public static List<Class<?>> getResourceDefClasses() {
|
||||
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
||||
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
|
||||
|
||||
List<Class<?>> classList = new ArrayList<>(classes.size());
|
||||
classes.forEach(o -> {
|
||||
Class<?> c = (Class<?>) o;
|
||||
if (c.getAnnotation(ResourceType.class) != null) {
|
||||
classList.add(c);
|
||||
}
|
||||
});
|
||||
List<Class<?>> classList = new ArrayList<>(classes.size());
|
||||
classes.forEach(o -> {
|
||||
Class<?> c = (Class<?>) o;
|
||||
if (c.getAnnotation(ResourceType.class) != null) {
|
||||
classList.add(c);
|
||||
}
|
||||
});
|
||||
|
||||
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
|
||||
classList.sort((a, b) -> b.getAnnotation(ResourceType.class).loadPriority().value() - a.getAnnotation(ResourceType.class).loadPriority().value());
|
||||
|
||||
return classList;
|
||||
}
|
||||
return classList;
|
||||
}
|
||||
|
||||
public static void loadAll() {
|
||||
public static void loadAll() {
|
||||
Grasscutter.getLogger().info(translate("messages.status.resources.loading"));
|
||||
|
||||
// Load ability lists
|
||||
loadAbilityEmbryos();
|
||||
loadOpenConfig();
|
||||
loadAbilityModifiers();
|
||||
// Load resources
|
||||
loadResources();
|
||||
// Process into depots
|
||||
GameDepot.load();
|
||||
// Load spawn data and quests
|
||||
loadSpawnData();
|
||||
loadQuests();
|
||||
// Load scene points - must be done AFTER resources are loaded
|
||||
loadScenePoints();
|
||||
// Load default home layout
|
||||
loadHomeworldDefaultSaveData();
|
||||
loadNpcBornData();
|
||||
// Load ability lists
|
||||
loadAbilityEmbryos();
|
||||
loadOpenConfig();
|
||||
loadAbilityModifiers();
|
||||
// Load resources
|
||||
loadResources();
|
||||
// Process into depots
|
||||
GameDepot.load();
|
||||
// Load spawn data and quests
|
||||
loadSpawnData();
|
||||
loadQuests();
|
||||
// Load scene points - must be done AFTER resources are loaded
|
||||
loadScenePoints();
|
||||
// Load default home layout
|
||||
loadHomeworldDefaultSaveData();
|
||||
loadNpcBornData();
|
||||
|
||||
Grasscutter.getLogger().info(translate("messages.status.resources.finish"));
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadResources() {
|
||||
loadResources(false);
|
||||
}
|
||||
public static void loadResources() {
|
||||
loadResources(false);
|
||||
}
|
||||
|
||||
public static void loadResources(boolean doReload) {
|
||||
for (Class<?> resourceDefinition : getResourceDefClasses()) {
|
||||
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
|
||||
public static void loadResources(boolean doReload) {
|
||||
for (Class<?> resourceDefinition : getResourceDefClasses()) {
|
||||
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
|
||||
|
||||
if (type == null) {
|
||||
continue;
|
||||
}
|
||||
if (type == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
|
||||
@SuppressWarnings("rawtypes")
|
||||
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
|
||||
|
||||
if (map == null) {
|
||||
continue;
|
||||
}
|
||||
if (map == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
loadFromResource(resourceDefinition, type, map, doReload);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
loadFromResource(resourceDefinition, type, map, doReload);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
|
||||
if(!loadedResources.contains(c.getSimpleName()) || doReload) {
|
||||
for (String name : type.name()) {
|
||||
loadFromResource(c, name, map);
|
||||
}
|
||||
loadedResources.add(c.getSimpleName());
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
|
||||
if (!loadedResources.contains(c.getSimpleName()) || doReload) {
|
||||
for (String name : type.name()) {
|
||||
loadFromResource(c, name, map);
|
||||
}
|
||||
loadedResources.add(c.getSimpleName());
|
||||
Grasscutter.getLogger().debug("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
|
||||
try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) {
|
||||
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
|
||||
try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) {
|
||||
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
|
||||
|
||||
for (Object o : list) {
|
||||
GameResource res = (GameResource) o;
|
||||
res.onLoad();
|
||||
map.put(res.getId(), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Object o : list) {
|
||||
GameResource res = (GameResource) o;
|
||||
res.onLoad();
|
||||
map.put(res.getId(), res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadScenePoints() {
|
||||
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
|
||||
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
|
||||
private static void loadScenePoints() {
|
||||
Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)");
|
||||
File folder = new File(RESOURCE("BinOutput/Scene/Point"));
|
||||
|
||||
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
|
||||
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
|
||||
return;
|
||||
}
|
||||
if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) {
|
||||
Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<ScenePointEntry> scenePointList = new ArrayList<>();
|
||||
for (File file : Objects.requireNonNull(folder.listFiles())) {
|
||||
ScenePointConfig config; Integer sceneId;
|
||||
List<ScenePointEntry> scenePointList = new ArrayList<>();
|
||||
for (File file : Objects.requireNonNull(folder.listFiles())) {
|
||||
ScenePointConfig config; Integer sceneId;
|
||||
|
||||
Matcher matcher = pattern.matcher(file.getName());
|
||||
if (matcher.find()) {
|
||||
sceneId = Integer.parseInt(matcher.group(1));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(file.getName());
|
||||
if (matcher.find()) {
|
||||
sceneId = Integer.parseInt(matcher.group(1));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, ScenePointConfig.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.points == null) {
|
||||
continue;
|
||||
}
|
||||
if (config.points == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
|
||||
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
|
||||
pointData.setId(Integer.parseInt(entry.getKey()));
|
||||
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
|
||||
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
|
||||
pointData.setId(Integer.parseInt(entry.getKey()));
|
||||
|
||||
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
|
||||
scenePointList.add(sl);
|
||||
GameData.getScenePointIdList().add(pointData.getId());
|
||||
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
|
||||
scenePointList.add(sl);
|
||||
GameData.getScenePointIdList().add(pointData.getId());
|
||||
|
||||
pointData.updateDailyDungeon();
|
||||
}
|
||||
pointData.updateDailyDungeon();
|
||||
}
|
||||
|
||||
for (ScenePointEntry entry : scenePointList) {
|
||||
GameData.getScenePointEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ScenePointEntry entry : scenePointList) {
|
||||
GameData.getScenePointEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAbilityEmbryos() {
|
||||
List<AbilityEmbryoEntry> embryoList = null;
|
||||
private static void loadAbilityEmbryos() {
|
||||
List<AbilityEmbryoEntry> embryoList = null;
|
||||
|
||||
// Read from cached file if exists
|
||||
try (InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
|
||||
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
|
||||
} catch (Exception ignored) {}
|
||||
// Read from cached file if exists
|
||||
try (InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) {
|
||||
embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if(embryoList == null) {
|
||||
// Load from BinOutput
|
||||
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
|
||||
if (embryoList == null) {
|
||||
// Load from BinOutput
|
||||
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
|
||||
|
||||
embryoList = new LinkedList<>();
|
||||
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
|
||||
File[] files = folder.listFiles();
|
||||
if(files == null) {
|
||||
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
embryoList = new LinkedList<>();
|
||||
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/")));
|
||||
File[] files = folder.listFiles();
|
||||
if (files == null) {
|
||||
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
AvatarConfig config;
|
||||
String avatarName;
|
||||
for (File file : files) {
|
||||
AvatarConfig config;
|
||||
String avatarName;
|
||||
|
||||
Matcher matcher = pattern.matcher(file.getName());
|
||||
if (matcher.find()) {
|
||||
avatarName = matcher.group(0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Matcher matcher = pattern.matcher(file.getName());
|
||||
if (matcher.find()) {
|
||||
avatarName = matcher.group(0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.abilities == null) {
|
||||
continue;
|
||||
}
|
||||
if (config.abilities == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int s = config.abilities.size();
|
||||
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
|
||||
embryoList.add(al);
|
||||
}
|
||||
int s = config.abilities.size();
|
||||
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
|
||||
embryoList.add(al);
|
||||
}
|
||||
|
||||
File playerElementsFile = new File(Utils.toFilePath(RESOURCE("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json")));
|
||||
File playerElementsFile = new File(Utils.toFilePath(RESOURCE("BinOutput/AbilityGroup/AbilityGroup_Other_PlayerElementAbility.json")));
|
||||
|
||||
if (playerElementsFile.exists()) {
|
||||
try (FileReader fileReader = new FileReader(playerElementsFile)) {
|
||||
GameDepot.setPlayerAbilities(Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<String, AvatarConfig>>(){}.getType()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (playerElementsFile.exists()) {
|
||||
try (FileReader fileReader = new FileReader(playerElementsFile)) {
|
||||
GameDepot.setPlayerAbilities(Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken<Map<String, AvatarConfig>>(){}.getType()));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (embryoList == null || embryoList.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No embryos loaded!");
|
||||
return;
|
||||
}
|
||||
if (embryoList == null || embryoList.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No embryos loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (AbilityEmbryoEntry entry : embryoList) {
|
||||
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
for (AbilityEmbryoEntry entry : embryoList) {
|
||||
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAbilityModifiers() {
|
||||
// Load from BinOutput
|
||||
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
|
||||
File[] files = folder.listFiles();
|
||||
if (files == null) {
|
||||
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
private static void loadAbilityModifiers() {
|
||||
// Load from BinOutput
|
||||
File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/")));
|
||||
File[] files = folder.listFiles();
|
||||
if (files == null) {
|
||||
Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
List<AbilityConfigData> abilityConfigList;
|
||||
for (File file : files) {
|
||||
List<AbilityConfigData> abilityConfigList;
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (AbilityConfigData data : abilityConfigList) {
|
||||
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
for (AbilityConfigData data : abilityConfigList) {
|
||||
if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
|
||||
AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName);
|
||||
|
||||
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
|
||||
AbilityModifier modifier = entry.getValue();
|
||||
for (Entry<String, AbilityModifier> entry : data.Default.modifiers.entrySet()) {
|
||||
AbilityModifier modifier = entry.getValue();
|
||||
|
||||
// Stare.
|
||||
if (modifier.onAdded != null) {
|
||||
for (AbilityModifierAction action : modifier.onAdded) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnAdded().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stare.
|
||||
if (modifier.onAdded != null) {
|
||||
for (AbilityModifierAction action : modifier.onAdded) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnAdded().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifier.onThinkInterval != null) {
|
||||
for (AbilityModifierAction action : modifier.onThinkInterval) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnThinkInterval().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modifier.onThinkInterval != null) {
|
||||
for (AbilityModifierAction action : modifier.onThinkInterval) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnThinkInterval().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (modifier.onRemoved != null) {
|
||||
for (AbilityModifierAction action : modifier.onRemoved) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnRemoved().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (modifier.onRemoved != null) {
|
||||
for (AbilityModifierAction action : modifier.onRemoved) {
|
||||
if (action.$type.contains("HealHP")) {
|
||||
action.type = AbilityModifierActionType.HealHP;
|
||||
modifierEntry.getOnRemoved().add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadSpawnData() {
|
||||
String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"};
|
||||
ArrayList<SpawnGroupEntry> spawnEntryMap = new ArrayList<>();
|
||||
private static void loadSpawnData() {
|
||||
String[] spawnDataNames = {"Spawns.json", "GadgetSpawns.json"};
|
||||
ArrayList<SpawnGroupEntry> spawnEntryMap = new ArrayList<>();
|
||||
|
||||
for (String name : spawnDataNames) {
|
||||
// Load spawn entries from file
|
||||
@ -321,10 +321,10 @@ public class ResourceLoader {
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (spawnEntryMap.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No spawn data loaded!");
|
||||
return;
|
||||
}
|
||||
if (spawnEntryMap.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No spawn data loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
HashMap<GridBlockId, ArrayList<SpawnDataEntry>> areaSort = new HashMap<>();
|
||||
//key = sceneId,x,z , value = ArrayList<SpawnDataEntry>
|
||||
@ -333,7 +333,7 @@ public class ResourceLoader {
|
||||
s -> {
|
||||
s.setGroup(entry);
|
||||
GridBlockId point = s.getBlockId();
|
||||
if(!areaSort.containsKey(point)) {
|
||||
if (!areaSort.containsKey(point)) {
|
||||
areaSort.put(point, new ArrayList<>());
|
||||
}
|
||||
areaSort.get(point).add(s);
|
||||
@ -341,155 +341,155 @@ public class ResourceLoader {
|
||||
);
|
||||
}
|
||||
GameDepot.addSpawnListById(areaSort);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadOpenConfig() {
|
||||
// Read from cached file if exists
|
||||
List<OpenConfigEntry> list = null;
|
||||
private static void loadOpenConfig() {
|
||||
// Read from cached file if exists
|
||||
List<OpenConfigEntry> list = null;
|
||||
|
||||
try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
|
||||
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
|
||||
} catch (Exception ignored) {}
|
||||
try (InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) {
|
||||
list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (list == null) {
|
||||
Map<String, OpenConfigEntry> map = new TreeMap<>();
|
||||
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
|
||||
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
|
||||
if (list == null) {
|
||||
Map<String, OpenConfigEntry> map = new TreeMap<>();
|
||||
java.lang.reflect.Type type = new TypeToken<Map<String, OpenConfigData[]>>() {}.getType();
|
||||
String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"};
|
||||
|
||||
for (String name : folderNames) {
|
||||
File folder = new File(Utils.toFilePath(RESOURCE(name)));
|
||||
File[] files = folder.listFiles();
|
||||
if(files == null) {
|
||||
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
|
||||
}
|
||||
for (String name : folderNames) {
|
||||
File folder = new File(Utils.toFilePath(RESOURCE(name)));
|
||||
File[] files = folder.listFiles();
|
||||
if (files == null) {
|
||||
Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return;
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
if (!file.getName().endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
for (File file : files) {
|
||||
if (!file.getName().endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<String, OpenConfigData[]> config;
|
||||
Map<String, OpenConfigData[]> config;
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, type);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, type);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
|
||||
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
|
||||
map.put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
|
||||
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
|
||||
map.put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list = new ArrayList<>(map.values());
|
||||
}
|
||||
list = new ArrayList<>(map.values());
|
||||
}
|
||||
|
||||
if (list == null || list.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No openconfig entries loaded!");
|
||||
return;
|
||||
}
|
||||
if (list == null || list.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No openconfig entries loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (OpenConfigEntry entry : list) {
|
||||
GameData.getOpenConfigEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
for (OpenConfigEntry entry : list) {
|
||||
GameData.getOpenConfigEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadQuests() {
|
||||
File folder = new File(RESOURCE("BinOutput/Quest/"));
|
||||
private static void loadQuests() {
|
||||
File folder = new File(RESOURCE("BinOutput/Quest/"));
|
||||
|
||||
if (!folder.exists()) {
|
||||
return;
|
||||
}
|
||||
if (!folder.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (File file : folder.listFiles()) {
|
||||
MainQuestData mainQuest = null;
|
||||
for (File file : folder.listFiles()) {
|
||||
MainQuestData mainQuest = null;
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
|
||||
}
|
||||
GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
|
||||
}
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void loadHomeworldDefaultSaveData(){
|
||||
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
|
||||
var pattern = Pattern.compile("scene(.*)_home_config.json");
|
||||
@SneakyThrows
|
||||
private static void loadHomeworldDefaultSaveData() {
|
||||
var folder = Files.list(Path.of(RESOURCE("BinOutput/HomeworldDefaultSave"))).toList();
|
||||
var pattern = Pattern.compile("scene(.*)_home_config.json");
|
||||
|
||||
for(var file : folder){
|
||||
var matcher = pattern.matcher(file.getFileName().toString());
|
||||
if(!matcher.find()){
|
||||
continue;
|
||||
}
|
||||
var sceneId = matcher.group(1);
|
||||
for (var file : folder) {
|
||||
var matcher = pattern.matcher(file.getFileName().toString());
|
||||
if (!matcher.find()) {
|
||||
continue;
|
||||
}
|
||||
var sceneId = matcher.group(1);
|
||||
|
||||
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
|
||||
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), HomeworldDefaultSaveData.class);
|
||||
|
||||
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
|
||||
}
|
||||
GameData.getHomeworldDefaultSaveData().put(Integer.parseInt(sceneId), data);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
|
||||
}
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getHomeworldDefaultSaveData().size() + " HomeworldDefaultSaveDatas.");
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void loadNpcBornData(){
|
||||
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
|
||||
@SneakyThrows
|
||||
private static void loadNpcBornData() {
|
||||
var folder = Files.list(Path.of(RESOURCE("BinOutput/Scene/SceneNpcBorn"))).toList();
|
||||
|
||||
for(var file : folder){
|
||||
if(file.toFile().isDirectory()){
|
||||
continue;
|
||||
}
|
||||
for (var file : folder) {
|
||||
if (file.toFile().isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
|
||||
if(data.getBornPosList() == null || data.getBornPosList().size() == 0){
|
||||
continue;
|
||||
}
|
||||
var data = Grasscutter.getGsonFactory().fromJson(Files.readString(file), SceneNpcBornData.class);
|
||||
if (data.getBornPosList() == null || data.getBornPosList().size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
|
||||
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
|
||||
}
|
||||
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
|
||||
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
|
||||
}
|
||||
Grasscutter.getLogger().debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
|
||||
}
|
||||
|
||||
// BinOutput configs
|
||||
// BinOutput configs
|
||||
|
||||
public static class AvatarConfig {
|
||||
@SerializedName(value="abilities", alternate={"targetAbilities"})
|
||||
public ArrayList<AvatarConfigAbility> abilities;
|
||||
}
|
||||
public static class AvatarConfig {
|
||||
@SerializedName(value="abilities", alternate={"targetAbilities"})
|
||||
public ArrayList<AvatarConfigAbility> abilities;
|
||||
}
|
||||
|
||||
public static class AvatarConfigAbility {
|
||||
public String abilityName;
|
||||
public String toString() {
|
||||
return abilityName;
|
||||
}
|
||||
}
|
||||
public static class AvatarConfigAbility {
|
||||
public String abilityName;
|
||||
public String toString() {
|
||||
return abilityName;
|
||||
}
|
||||
}
|
||||
|
||||
private static class OpenConfig {
|
||||
public OpenConfigData[] data;
|
||||
}
|
||||
private static class OpenConfig {
|
||||
public OpenConfigData[] data;
|
||||
}
|
||||
|
||||
public static class OpenConfigData {
|
||||
public String $type;
|
||||
public String abilityName;
|
||||
public static class OpenConfigData {
|
||||
public String $type;
|
||||
public String abilityName;
|
||||
|
||||
@SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"})
|
||||
public int talentIndex;
|
||||
@SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"})
|
||||
public int talentIndex;
|
||||
|
||||
@SerializedName(value="skillID", alternate={"overtime"})
|
||||
public int skillID;
|
||||
@SerializedName(value="skillID", alternate={"overtime"})
|
||||
public int skillID;
|
||||
|
||||
@SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"})
|
||||
public int pointDelta;
|
||||
}
|
||||
@SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"})
|
||||
public int pointDelta;
|
||||
}
|
||||
}
|
||||
|
@ -4,32 +4,32 @@ import com.google.gson.annotations.SerializedName;
|
||||
|
||||
// Used in excels
|
||||
public class ItemParamData {
|
||||
@SerializedName(value="id", alternate={"itemId"})
|
||||
private int id;
|
||||
|
||||
@SerializedName(value="count", alternate={"itemCount"})
|
||||
@SerializedName(value="id", alternate={"itemId"})
|
||||
private int id;
|
||||
|
||||
@SerializedName(value="count", alternate={"itemCount"})
|
||||
private int count;
|
||||
|
||||
public ItemParamData() {}
|
||||
|
||||
public ItemParamData(int id, int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public ItemParamData(int id, int count) {
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public int getItemCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
@ -1,35 +1,35 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
|
||||
@ResourceType(name = "EnvAnimalGatherExcelConfigData.json", loadPriority = ResourceType.LoadPriority.LOW)
|
||||
public class EnvAnimalGatherConfigData extends GameResource {
|
||||
private int animalId;
|
||||
private String entityType;
|
||||
private List<ItemParamData> gatherItemId;
|
||||
private String excludeWeathers;
|
||||
private int aliveTime;
|
||||
private int escapeTime;
|
||||
private int escapeRadius;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public int getAnimalId(){
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public String getEntityType(){
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public ItemParamData getGatherItem() {
|
||||
return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
|
||||
@ResourceType(name = "EnvAnimalGatherExcelConfigData.json", loadPriority = ResourceType.LoadPriority.LOW)
|
||||
public class EnvAnimalGatherConfigData extends GameResource {
|
||||
private int animalId;
|
||||
private String entityType;
|
||||
private List<ItemParamData> gatherItemId;
|
||||
private String excludeWeathers;
|
||||
private int aliveTime;
|
||||
private int escapeTime;
|
||||
private int escapeRadius;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public int getAnimalId() {
|
||||
return animalId;
|
||||
}
|
||||
|
||||
public String getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public ItemParamData getGatherItem() {
|
||||
return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
|
||||
}
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ public class ForgeData extends GameResource {
|
||||
private List<ItemParamData> materialItems;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getPlayerLevel() {
|
||||
return playerLevel;
|
||||
|
@ -12,58 +12,58 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
@ResourceType(name = "ReliquaryLevelExcelConfigData.json")
|
||||
public class ReliquaryLevelData extends GameResource {
|
||||
private int id;
|
||||
private Int2FloatMap propMap;
|
||||
|
||||
private int rank;
|
||||
private int level;
|
||||
private int exp;
|
||||
private List<RelicLevelProperty> addProps;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public float getPropValue(FightProperty prop) {
|
||||
return getPropValue(prop.getId());
|
||||
}
|
||||
|
||||
public float getPropValue(int id) {
|
||||
return propMap.getOrDefault(id, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.id = (rank << 8) + this.getLevel();
|
||||
this.propMap = new Int2FloatOpenHashMap();
|
||||
for (RelicLevelProperty p : addProps) {
|
||||
this.propMap.put(FightProperty.getPropByName(p.getPropType()).getId(), p.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public class RelicLevelProperty {
|
||||
private String propType;
|
||||
private float value;
|
||||
|
||||
public String getPropType() {
|
||||
return propType;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
private int id;
|
||||
private Int2FloatMap propMap;
|
||||
|
||||
private int rank;
|
||||
private int level;
|
||||
private int exp;
|
||||
private List<RelicLevelProperty> addProps;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return exp;
|
||||
}
|
||||
|
||||
public float getPropValue(FightProperty prop) {
|
||||
return getPropValue(prop.getId());
|
||||
}
|
||||
|
||||
public float getPropValue(int id) {
|
||||
return propMap.getOrDefault(id, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.id = (rank << 8) + this.getLevel();
|
||||
this.propMap = new Int2FloatOpenHashMap();
|
||||
for (RelicLevelProperty p : addProps) {
|
||||
this.propMap.put(FightProperty.getPropByName(p.getPropType()).getId(), p.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public class RelicLevelProperty {
|
||||
private String propType;
|
||||
private float value;
|
||||
|
||||
public String getPropType() {
|
||||
return propType;
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,98 +29,98 @@ import emu.grasscutter.game.quest.GameMainQuest;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
|
||||
public final class DatabaseManager {
|
||||
private static Datastore gameDatastore;
|
||||
private static Datastore dispatchDatastore;
|
||||
private static Datastore gameDatastore;
|
||||
private static Datastore dispatchDatastore;
|
||||
|
||||
private static final Class<?>[] mappedClasses = new Class<?>[] {
|
||||
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
|
||||
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class,
|
||||
private static final Class<?>[] mappedClasses = new Class<?>[] {
|
||||
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
|
||||
GachaRecord.class, Mail.class, GameMainQuest.class, GameHome.class, BattlePassManager.class,
|
||||
PlayerActivityData.class, MusicGameBeatmap.class
|
||||
};
|
||||
};
|
||||
|
||||
public static Datastore getGameDatastore() {
|
||||
return gameDatastore;
|
||||
return gameDatastore;
|
||||
}
|
||||
|
||||
public static MongoDatabase getGameDatabase() {
|
||||
return getGameDatastore().getDatabase();
|
||||
return getGameDatastore().getDatabase();
|
||||
}
|
||||
|
||||
// Yes. I very dislike this method. However, this will be good for now.
|
||||
// TODO: Add dispatch routes for player account management
|
||||
public static Datastore getAccountDatastore() {
|
||||
if(SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
return dispatchDatastore;
|
||||
} else {
|
||||
return gameDatastore;
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Initialize
|
||||
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
|
||||
|
||||
// Set mapper options.
|
||||
MapperOptions mapperOptions = MapperOptions.builder()
|
||||
.storeEmpties(true).storeNulls(false).build();
|
||||
// Create data store.
|
||||
gameDatastore = Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
|
||||
// Map classes.
|
||||
gameDatastore.getMapper().map(mappedClasses);
|
||||
|
||||
// Ensure indexes
|
||||
try {
|
||||
gameDatastore.ensureIndexes();
|
||||
} catch (MongoCommandException exception) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", exception);
|
||||
// Duplicate index error
|
||||
if (exception.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = gameDatastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
gameDatastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
gameDatastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
// Yes. I very dislike this method. However, this will be good for now.
|
||||
// TODO: Add dispatch routes for player account management
|
||||
public static Datastore getAccountDatastore() {
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
return dispatchDatastore;
|
||||
} else {
|
||||
return gameDatastore;
|
||||
}
|
||||
}
|
||||
|
||||
if(SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
|
||||
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection);
|
||||
public static void initialize() {
|
||||
// Initialize
|
||||
MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri);
|
||||
|
||||
// Ensure indexes for dispatch server
|
||||
try {
|
||||
dispatchDatastore.ensureIndexes();
|
||||
} catch (MongoCommandException e) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", e);
|
||||
// Duplicate index error
|
||||
if (e.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = dispatchDatastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
dispatchDatastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set mapper options.
|
||||
MapperOptions mapperOptions = MapperOptions.builder()
|
||||
.storeEmpties(true).storeNulls(false).build();
|
||||
// Create data store.
|
||||
gameDatastore = Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions);
|
||||
// Map classes.
|
||||
gameDatastore.getMapper().map(mappedClasses);
|
||||
|
||||
public static synchronized int getNextId(Class<?> c) {
|
||||
DatabaseCounter counter = getGameDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first();
|
||||
if (counter == null) {
|
||||
counter = new DatabaseCounter(c.getSimpleName());
|
||||
}
|
||||
try {
|
||||
return counter.getNextId();
|
||||
} finally {
|
||||
getGameDatastore().save(counter);
|
||||
}
|
||||
}
|
||||
// Ensure indexes
|
||||
try {
|
||||
gameDatastore.ensureIndexes();
|
||||
} catch (MongoCommandException exception) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", exception);
|
||||
// Duplicate index error
|
||||
if (exception.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = gameDatastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
gameDatastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
gameDatastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Object o) {
|
||||
return getNextId(o.getClass());
|
||||
}
|
||||
}
|
||||
if (SERVER.runMode == ServerRunMode.GAME_ONLY) {
|
||||
MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri);
|
||||
dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection);
|
||||
|
||||
// Ensure indexes for dispatch server
|
||||
try {
|
||||
dispatchDatastore.ensureIndexes();
|
||||
} catch (MongoCommandException e) {
|
||||
Grasscutter.getLogger().info("Mongo index error: ", e);
|
||||
// Duplicate index error
|
||||
if (e.getCode() == 85) {
|
||||
// Drop all indexes and re add them
|
||||
MongoIterable<String> collections = dispatchDatastore.getDatabase().listCollectionNames();
|
||||
for (String name : collections) {
|
||||
dispatchDatastore.getDatabase().getCollection(name).dropIndexes();
|
||||
}
|
||||
// Add back indexes
|
||||
dispatchDatastore.ensureIndexes();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Class<?> c) {
|
||||
DatabaseCounter counter = getGameDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first();
|
||||
if (counter == null) {
|
||||
counter = new DatabaseCounter(c.getSimpleName());
|
||||
}
|
||||
try {
|
||||
return counter.getNextId();
|
||||
} finally {
|
||||
getGameDatastore().save(counter);
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int getNextId(Object o) {
|
||||
return getNextId(o.getClass());
|
||||
}
|
||||
}
|
||||
|
@ -14,93 +14,93 @@ import org.bson.Document;
|
||||
|
||||
@Entity(value = "accounts", useDiscriminator = false)
|
||||
public class Account {
|
||||
@Id private String id;
|
||||
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
@Collation(locale = "simple", caseLevel = true)
|
||||
private String username;
|
||||
private String password; // Unused for now
|
||||
|
||||
private int reservedPlayerId;
|
||||
private String email;
|
||||
|
||||
private String token;
|
||||
private String sessionKey; // Session token for dispatch server
|
||||
private List<String> permissions;
|
||||
@Id private String id;
|
||||
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
@Collation(locale = "simple", caseLevel = true)
|
||||
private String username;
|
||||
private String password; // Unused for now
|
||||
|
||||
private int reservedPlayerId;
|
||||
private String email;
|
||||
|
||||
private String token;
|
||||
private String sessionKey; // Session token for dispatch server
|
||||
private List<String> permissions;
|
||||
private Locale locale;
|
||||
|
||||
private String banReason;
|
||||
private int banEndTime;
|
||||
private int banStartTime;
|
||||
private boolean isBanned;
|
||||
|
||||
@Deprecated
|
||||
public Account() {
|
||||
this.permissions = new ArrayList<>();
|
||||
private String banReason;
|
||||
private int banEndTime;
|
||||
private int banStartTime;
|
||||
private boolean isBanned;
|
||||
|
||||
@Deprecated
|
||||
public Account() {
|
||||
this.permissions = new ArrayList<>();
|
||||
this.locale = LANGUAGE;
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public int getReservedPlayerUid() {
|
||||
return this.reservedPlayerId;
|
||||
}
|
||||
public int getReservedPlayerUid() {
|
||||
return this.reservedPlayerId;
|
||||
}
|
||||
|
||||
public void setReservedPlayerUid(int playerId) {
|
||||
this.reservedPlayerId = playerId;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
if(email != null && !email.isEmpty()) {
|
||||
return email;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
public void setReservedPlayerUid(int playerId) {
|
||||
this.reservedPlayerId = playerId;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
public String getEmail() {
|
||||
if (email != null && !email.isEmpty()) {
|
||||
return email;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String getSessionKey() {
|
||||
return this.sessionKey;
|
||||
}
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String generateSessionKey() {
|
||||
this.sessionKey = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
this.save();
|
||||
return this.sessionKey;
|
||||
}
|
||||
public String getSessionKey() {
|
||||
return this.sessionKey;
|
||||
}
|
||||
|
||||
public String generateSessionKey() {
|
||||
this.sessionKey = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
this.save();
|
||||
return this.sessionKey;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
@ -110,126 +110,126 @@ public class Account {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public String getBanReason() {
|
||||
return banReason;
|
||||
}
|
||||
public String getBanReason() {
|
||||
return banReason;
|
||||
}
|
||||
|
||||
public void setBanReason(String banReason) {
|
||||
this.banReason = banReason;
|
||||
}
|
||||
public void setBanReason(String banReason) {
|
||||
this.banReason = banReason;
|
||||
}
|
||||
|
||||
public int getBanEndTime() {
|
||||
return banEndTime;
|
||||
}
|
||||
public int getBanEndTime() {
|
||||
return banEndTime;
|
||||
}
|
||||
|
||||
public void setBanEndTime(int banEndTime) {
|
||||
this.banEndTime = banEndTime;
|
||||
}
|
||||
public void setBanEndTime(int banEndTime) {
|
||||
this.banEndTime = banEndTime;
|
||||
}
|
||||
|
||||
public int getBanStartTime() {
|
||||
return banStartTime;
|
||||
}
|
||||
public int getBanStartTime() {
|
||||
return banStartTime;
|
||||
}
|
||||
|
||||
public void setBanStartTime(int banStartTime) {
|
||||
this.banStartTime = banStartTime;
|
||||
}
|
||||
public void setBanStartTime(int banStartTime) {
|
||||
this.banStartTime = banStartTime;
|
||||
}
|
||||
|
||||
public boolean isBanned() {
|
||||
if (banEndTime > 0 && banEndTime < System.currentTimeMillis() / 1000) {
|
||||
this.isBanned = false;
|
||||
this.banEndTime = 0;
|
||||
this.banStartTime = 0;
|
||||
this.banReason = null;
|
||||
save();
|
||||
}
|
||||
public boolean isBanned() {
|
||||
if (banEndTime > 0 && banEndTime < System.currentTimeMillis() / 1000) {
|
||||
this.isBanned = false;
|
||||
this.banEndTime = 0;
|
||||
this.banStartTime = 0;
|
||||
this.banReason = null;
|
||||
save();
|
||||
}
|
||||
|
||||
return isBanned;
|
||||
}
|
||||
return isBanned;
|
||||
}
|
||||
|
||||
public void setBanned(boolean isBanned) {
|
||||
this.isBanned = isBanned;
|
||||
}
|
||||
public void setBanned(boolean isBanned) {
|
||||
this.isBanned = isBanned;
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection of a player's permissions.
|
||||
*/
|
||||
public List<String> getPermissions() {
|
||||
return this.permissions;
|
||||
}
|
||||
|
||||
public boolean addPermission(String permission) {
|
||||
if(this.permissions.contains(permission)) return false;
|
||||
this.permissions.add(permission); return true;
|
||||
}
|
||||
/**
|
||||
* The collection of a player's permissions.
|
||||
*/
|
||||
public List<String> getPermissions() {
|
||||
return this.permissions;
|
||||
}
|
||||
|
||||
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
|
||||
String[] wildcardParts = wildcard.split("\\.");
|
||||
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<wildcardParts.length; i++) {
|
||||
switch (wildcardParts[i]) {
|
||||
case "**": // Recursing match
|
||||
return true;
|
||||
case "*": // Match only one layer
|
||||
if (i >= (permissionParts.length-1)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default: // This layer isn't a wildcard, it needs to match exactly
|
||||
if (!wildcardParts[i].equals(permissionParts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
|
||||
return (wildcardParts.length == permissionParts.length);
|
||||
}
|
||||
public boolean addPermission(String permission) {
|
||||
if (this.permissions.contains(permission)) return false;
|
||||
this.permissions.add(permission); return true;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String permission) {
|
||||
if(this.permissions.contains("*") && this.permissions.size() == 1) return true;
|
||||
public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) {
|
||||
String[] wildcardParts = wildcard.split("\\.");
|
||||
if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission
|
||||
return false;
|
||||
}
|
||||
for (int i=0; i<wildcardParts.length; i++) {
|
||||
switch (wildcardParts[i]) {
|
||||
case "**": // Recursing match
|
||||
return true;
|
||||
case "*": // Match only one layer
|
||||
if (i >= (permissionParts.length-1)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default: // This layer isn't a wildcard, it needs to match exactly
|
||||
if (!wildcardParts[i].equals(permissionParts[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
|
||||
return (wildcardParts.length == permissionParts.length);
|
||||
}
|
||||
|
||||
// Add default permissions if it doesn't exist
|
||||
List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
|
||||
.flatMap(Collection::stream)
|
||||
.distinct().toList();
|
||||
public boolean hasPermission(String permission) {
|
||||
if (this.permissions.contains("*") && this.permissions.size() == 1) return true;
|
||||
|
||||
if (permissions.contains(permission)) return true;
|
||||
// Add default permissions if it doesn't exist
|
||||
List<String> permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions))
|
||||
.flatMap(Collection::stream)
|
||||
.distinct().toList();
|
||||
|
||||
String[] permissionParts = permission.split("\\.");
|
||||
for (String p : permissions) {
|
||||
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
|
||||
if (permissionMatchesWildcard(p, permissionParts)) return true;
|
||||
}
|
||||
if (permissions.contains(permission)) return true;
|
||||
|
||||
return permissions.contains("*");
|
||||
}
|
||||
String[] permissionParts = permission.split("\\.");
|
||||
for (String p : permissions) {
|
||||
if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false;
|
||||
if (permissionMatchesWildcard(p, permissionParts)) return true;
|
||||
}
|
||||
|
||||
public boolean removePermission(String permission) {
|
||||
return this.permissions.remove(permission);
|
||||
}
|
||||
return permissions.contains("*");
|
||||
}
|
||||
|
||||
// TODO make unique
|
||||
public String generateLoginToken() {
|
||||
this.token = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
this.save();
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveAccount(this);
|
||||
}
|
||||
public boolean removePermission(String permission) {
|
||||
return this.permissions.remove(permission);
|
||||
}
|
||||
|
||||
@PreLoad
|
||||
public void onLoad(Document document) {
|
||||
// Grant the superuser permissions to accounts created before the permissions update
|
||||
if (!document.containsKey("permissions")) {
|
||||
this.addPermission("*");
|
||||
}
|
||||
// TODO make unique
|
||||
public String generateLoginToken() {
|
||||
this.token = Utils.bytesToHex(Crypto.createSessionKey(32));
|
||||
this.save();
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveAccount(this);
|
||||
}
|
||||
|
||||
@PreLoad
|
||||
public void onLoad(Document document) {
|
||||
// Grant the superuser permissions to accounts created before the permissions update
|
||||
if (!document.containsKey("permissions")) {
|
||||
this.addPermission("*");
|
||||
}
|
||||
|
||||
// Set account default language as server default language
|
||||
if (!document.containsKey("locale")) {
|
||||
this.locale = LANGUAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,158 +20,158 @@ import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||
|
||||
public class AbilityManager extends BasePlayerManager {
|
||||
HealAbilityManager healAbilityManager;
|
||||
|
||||
public AbilityManager(Player player) {
|
||||
super(player);
|
||||
this.healAbilityManager = new HealAbilityManager(player);
|
||||
}
|
||||
|
||||
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
|
||||
public AbilityManager(Player player) {
|
||||
super(player);
|
||||
this.healAbilityManager = new HealAbilityManager(player);
|
||||
}
|
||||
|
||||
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
|
||||
healAbilityManager.healHandler(invoke);
|
||||
|
||||
//Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
|
||||
switch (invoke.getArgumentType()) {
|
||||
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
|
||||
handleOverrideParam(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP:
|
||||
handleReinitOverrideMap(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
|
||||
handleModifierChange(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
|
||||
handleMixinCostStamina(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL:
|
||||
handleGenerateElemBall(invoke);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
|
||||
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
|
||||
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
|
||||
|
||||
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
|
||||
// Sanity checks
|
||||
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroying rocks
|
||||
if (target instanceof EntityGadget targetGadget && targetGadget.getContent() instanceof GadgetGatherObject gatherObject) {
|
||||
if (data.getAction() == ModifierAction.REMOVED) {
|
||||
gatherObject.dropItems(this.getPlayer());
|
||||
return;
|
||||
}
|
||||
//Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
|
||||
switch (invoke.getArgumentType()) {
|
||||
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
|
||||
handleOverrideParam(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP:
|
||||
handleReinitOverrideMap(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
|
||||
handleModifierChange(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
|
||||
handleMixinCostStamina(invoke);
|
||||
break;
|
||||
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL:
|
||||
handleGenerateElemBall(invoke);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
AbilityInvokeEntryHead head = invoke.getHead();
|
||||
if (head == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
|
||||
if (sourceEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
|
||||
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
|
||||
// Handle add modifier here
|
||||
String modifierString = data.getParentAbilityName().getStr();
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnAdded().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnAdded()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to meta modifier list
|
||||
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
|
||||
} else if (data.getAction() == ModifierAction.REMOVED) {
|
||||
// Handle remove modifier
|
||||
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
|
||||
|
||||
if (modifierString != null) {
|
||||
// Get modifier and call on remove event
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnRemoved().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnRemoved()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from meta modifiers
|
||||
target.getMetaModifiers().remove(head.getInstancedModifierId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
|
||||
AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData()));
|
||||
getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
|
||||
}
|
||||
|
||||
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
|
||||
this.player.getEnergyManager().handleGenerateElemBall(invoke);
|
||||
}
|
||||
|
||||
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
case HealHP -> {
|
||||
}
|
||||
case LoseHP -> {
|
||||
if (action.amountByTargetCurrentHPRatio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float damageAmount = 0;
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
|
||||
if (damageAmount > 0) {
|
||||
target.damage(damageAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
|
||||
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
|
||||
private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception {
|
||||
GameEntity entity = player.getScene().getEntityById(invoke.getEntityId());
|
||||
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData());
|
||||
|
||||
for (AbilityScalarValueEntry entry : map.getOverrideMapList()) {
|
||||
entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception {
|
||||
// Sanity checks
|
||||
GameEntity target = player.getScene().getEntityById(invoke.getEntityId());
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroying rocks
|
||||
if (target instanceof EntityGadget targetGadget && targetGadget.getContent() instanceof GadgetGatherObject gatherObject) {
|
||||
if (data.getAction() == ModifierAction.REMOVED) {
|
||||
gatherObject.dropItems(this.getPlayer());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity checks
|
||||
AbilityInvokeEntryHead head = invoke.getHead();
|
||||
if (head == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
|
||||
if (sourceEntity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not how it works but we will keep it for now since healing abilities dont work properly anyways
|
||||
if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) {
|
||||
// Handle add modifier here
|
||||
String modifierString = data.getParentAbilityName().getStr();
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnAdded().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnAdded()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to meta modifier list
|
||||
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
|
||||
} else if (data.getAction() == ModifierAction.REMOVED) {
|
||||
// Handle remove modifier
|
||||
String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId());
|
||||
|
||||
if (modifierString != null) {
|
||||
// Get modifier and call on remove event
|
||||
AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString);
|
||||
|
||||
if (modifier != null && modifier.getOnRemoved().size() > 0) {
|
||||
for (AbilityModifierAction action : modifier.getOnRemoved()) {
|
||||
invokeAction(action, target, sourceEntity);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from meta modifiers
|
||||
target.getMetaModifiers().remove(head.getInstancedModifierId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
|
||||
AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData()));
|
||||
getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
|
||||
}
|
||||
|
||||
private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException {
|
||||
this.player.getEnergyManager().handleGenerateElemBall(invoke);
|
||||
}
|
||||
|
||||
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
case HealHP -> {
|
||||
}
|
||||
case LoseHP -> {
|
||||
if (action.amountByTargetCurrentHPRatio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float damageAmount = 0;
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
|
||||
if (damageAmount > 0) {
|
||||
target.damage(damageAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class ActivityManager extends BasePlayerManager {
|
||||
activityWatcherTypeMap.put(typeName.value(), ConstructorAccess.get(item));
|
||||
});
|
||||
|
||||
try(Reader reader = DataLoader.loadReader("ActivityConfig.json")) {
|
||||
try (Reader reader = DataLoader.loadReader("ActivityConfig.json")) {
|
||||
List<ActivityConfigItem> activities = Grasscutter.getGsonFactory().fromJson(
|
||||
reader,
|
||||
TypeToken.getParameterized(List.class, ActivityConfigItem.class).getType());
|
||||
@ -53,16 +53,16 @@ public class ActivityManager extends BasePlayerManager {
|
||||
|
||||
activities.forEach(item -> {
|
||||
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
|
||||
if(activityData == null){
|
||||
if (activityData == null) {
|
||||
Grasscutter.getLogger().warn("activity {} not exist.", item.getActivityId());
|
||||
return;
|
||||
}
|
||||
var activityHandlerType = activityHandlerTypeMap.get(ActivityType.getTypeByName(activityData.getActivityType()));
|
||||
ActivityHandler activityHandler;
|
||||
|
||||
if(activityHandlerType != null) {
|
||||
if (activityHandlerType != null) {
|
||||
activityHandler = (ActivityHandler) activityHandlerType.newInstance();
|
||||
}else{
|
||||
}else {
|
||||
activityHandler = new DefaultActivityHandler();
|
||||
}
|
||||
activityHandler.setActivityConfigItem(item);
|
||||
@ -79,14 +79,14 @@ public class ActivityManager extends BasePlayerManager {
|
||||
|
||||
}
|
||||
|
||||
public ActivityManager(Player player){
|
||||
public ActivityManager(Player player) {
|
||||
super(player);
|
||||
|
||||
playerActivityDataMap = new ConcurrentHashMap<>();
|
||||
// load data for player
|
||||
activityConfigItemMap.values().forEach(item -> {
|
||||
var data = PlayerActivityData.getByPlayer(player, item.getActivityId());
|
||||
if(data == null){
|
||||
if (data == null) {
|
||||
data = item.getActivityHandler().initPlayerActivityData(player);
|
||||
data.save();
|
||||
}
|
||||
@ -116,34 +116,34 @@ public class ActivityManager extends BasePlayerManager {
|
||||
params));
|
||||
}
|
||||
|
||||
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId){
|
||||
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) {
|
||||
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
|
||||
var activityData = playerActivityDataMap.get(activityId);
|
||||
|
||||
return activityHandler.toProto(activityData);
|
||||
}
|
||||
|
||||
public Optional<ActivityHandler> getActivityHandler(ActivityType type){
|
||||
public Optional<ActivityHandler> getActivityHandler(ActivityType type) {
|
||||
return activityConfigItemMap.values().stream()
|
||||
.map(ActivityConfigItem::getActivityHandler)
|
||||
.filter(x -> type == x.getClass().getAnnotation(GameActivity.class).value())
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public <T extends ActivityHandler> Optional<T> getActivityHandlerAs(ActivityType type, Class<T> clazz){
|
||||
public <T extends ActivityHandler> Optional<T> getActivityHandlerAs(ActivityType type, Class<T> clazz) {
|
||||
return getActivityHandler(type).map(x -> (T)x);
|
||||
}
|
||||
|
||||
public Optional<Integer> getActivityIdByActivityType(ActivityType type){
|
||||
public Optional<Integer> getActivityIdByActivityType(ActivityType type) {
|
||||
return getActivityHandler(type)
|
||||
.map(ActivityHandler::getActivityConfigItem)
|
||||
.map(ActivityConfigItem::getActivityId);
|
||||
}
|
||||
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type){
|
||||
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type) {
|
||||
return getActivityIdByActivityType(type)
|
||||
.map(playerActivityDataMap::get);
|
||||
}
|
||||
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(ActivityType type){
|
||||
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(ActivityType type) {
|
||||
return getActivityIdByActivityType(type)
|
||||
.map(this::getInfoProtoByActivityId);
|
||||
}
|
||||
|
@ -41,8 +41,8 @@ public class MusicGameActivityHandler extends ActivityHandler {
|
||||
.build());
|
||||
}
|
||||
|
||||
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData){
|
||||
if(playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()){
|
||||
public MusicGamePlayerData getMusicGamePlayerData(PlayerActivityData playerActivityData) {
|
||||
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
|
||||
onInitPlayerActivityData(playerActivityData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class MusicGameActivityHandler extends ActivityHandler {
|
||||
MusicGamePlayerData.class);
|
||||
}
|
||||
|
||||
public boolean setMusicGameRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord){
|
||||
public boolean setMusicGameRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
|
||||
|
||||
@ -63,7 +63,7 @@ public class MusicGameActivityHandler extends ActivityHandler {
|
||||
|
||||
return newRecord.getMaxScore() > saveRecord.getMaxScore();
|
||||
}
|
||||
public void setMusicGameCustomBeatmapRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord){
|
||||
public void setMusicGameCustomBeatmapRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord) {
|
||||
var musicGamePlayerData = getMusicGamePlayerData(playerActivityData);
|
||||
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class MusicGamePlayerData {
|
||||
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
|
||||
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
|
||||
|
||||
public static MusicGamePlayerData create(){
|
||||
public static MusicGamePlayerData create() {
|
||||
return MusicGamePlayerData.of()
|
||||
.musicGameRecord(GameData.getMusicGameBasicDataMap().values().stream()
|
||||
.collect(Collectors.toMap(MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
|
||||
@ -38,13 +38,13 @@ public class MusicGamePlayerData {
|
||||
int maxCombo;
|
||||
int maxScore;
|
||||
|
||||
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData){
|
||||
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData) {
|
||||
return MusicGameRecord.of()
|
||||
.musicId(musicGameBasicData.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
public MusicGameRecordOuterClass.MusicGameRecord toProto(){
|
||||
public MusicGameRecordOuterClass.MusicGameRecord toProto() {
|
||||
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
|
||||
.setIsUnlock(true)
|
||||
.setMaxCombo(maxCombo)
|
||||
@ -61,7 +61,7 @@ public class MusicGamePlayerData {
|
||||
int score;
|
||||
boolean settle;
|
||||
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toPersonalBriefProto(){
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toPersonalBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return MusicBriefInfoOuterClass.MusicBriefInfo.newBuilder()
|
||||
@ -74,7 +74,7 @@ public class MusicGamePlayerData {
|
||||
.setMusicShareId(musicShareId);
|
||||
}
|
||||
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toOthersBriefProto(){
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toOthersBriefProto() {
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return musicGameBeatmap.toBriefProto()
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,155 +19,155 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
||||
public class AvatarStorage extends BasePlayerManager implements Iterable<Avatar> {
|
||||
private final Int2ObjectMap<Avatar> avatars;
|
||||
private final Long2ObjectMap<Avatar> avatarsGuid;
|
||||
|
||||
public AvatarStorage(Player player) {
|
||||
super(player);
|
||||
this.avatars = new Int2ObjectOpenHashMap<>();
|
||||
this.avatarsGuid = new Long2ObjectOpenHashMap<>();
|
||||
}
|
||||
private final Int2ObjectMap<Avatar> avatars;
|
||||
private final Long2ObjectMap<Avatar> avatarsGuid;
|
||||
|
||||
public Int2ObjectMap<Avatar> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public int getAvatarCount() {
|
||||
return this.avatars.size();
|
||||
}
|
||||
|
||||
public Avatar getAvatarById(int id) {
|
||||
return getAvatars().get(id);
|
||||
}
|
||||
|
||||
public Avatar getAvatarByGuid(long id) {
|
||||
return avatarsGuid.get(id);
|
||||
}
|
||||
|
||||
public boolean hasAvatar(int id) {
|
||||
return getAvatars().containsKey(id);
|
||||
}
|
||||
|
||||
public boolean addAvatar(Avatar avatar) {
|
||||
if (avatar.getAvatarData() == null || this.hasAvatar(avatar.getAvatarId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set owner first
|
||||
avatar.setOwner(getPlayer());
|
||||
public AvatarStorage(Player player) {
|
||||
super(player);
|
||||
this.avatars = new Int2ObjectOpenHashMap<>();
|
||||
this.avatarsGuid = new Long2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
// Put into maps
|
||||
this.avatars.put(avatar.getAvatarId(), avatar);
|
||||
this.avatarsGuid.put(avatar.getGuid(), avatar);
|
||||
public Int2ObjectMap<Avatar> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
avatar.save();
|
||||
public int getAvatarCount() {
|
||||
return this.avatars.size();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addStartingWeapon(Avatar avatar) {
|
||||
// Make sure avatar owner is this player
|
||||
if (avatar.getPlayer() != this.getPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create weapon
|
||||
GameItem weapon = new GameItem(avatar.getAvatarData().getInitialWeapon());
|
||||
|
||||
if (weapon.getItemData() != null) {
|
||||
this.getPlayer().getInventory().addItem(weapon);
|
||||
|
||||
avatar.equipItem(weapon, true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wearFlycloak(long avatarGuid, int flycloakId) {
|
||||
Avatar avatar = this.getAvatarByGuid(avatarGuid);
|
||||
public Avatar getAvatarById(int id) {
|
||||
return getAvatars().get(id);
|
||||
}
|
||||
|
||||
if (avatar == null || !getPlayer().getFlyCloakList().contains(flycloakId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
avatar.setFlyCloak(flycloakId);
|
||||
avatar.save();
|
||||
|
||||
// Update
|
||||
getPlayer().sendPacket(new PacketAvatarFlycloakChangeNotify(avatar));
|
||||
public Avatar getAvatarByGuid(long id) {
|
||||
return avatarsGuid.get(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean changeCostume(long avatarGuid, int costumeId) {
|
||||
Avatar avatar = this.getAvatarByGuid(avatarGuid);
|
||||
public boolean hasAvatar(int id) {
|
||||
return getAvatars().containsKey(id);
|
||||
}
|
||||
|
||||
if (avatar == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (costumeId != 0 && !getPlayer().getCostumeList().contains(costumeId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO make sure avatar can wear costume
|
||||
|
||||
avatar.setCostume(costumeId);
|
||||
avatar.save();
|
||||
public boolean addAvatar(Avatar avatar) {
|
||||
if (avatar.getAvatarData() == null || this.hasAvatar(avatar.getAvatarId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update entity
|
||||
EntityAvatar entity = avatar.getAsEntity();
|
||||
if (entity == null) {
|
||||
entity = new EntityAvatar(avatar);
|
||||
getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
|
||||
} else {
|
||||
getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
|
||||
}
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<Avatar> avatars = DatabaseHelper.getAvatars(getPlayer());
|
||||
|
||||
for (Avatar avatar : avatars) {
|
||||
// Should never happen
|
||||
if (avatar.getObjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
|
||||
if (avatarData == null || skillDepot == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set ownerships
|
||||
avatar.setAvatarData(avatarData);
|
||||
avatar.setSkillDepot(skillDepot);
|
||||
avatar.setOwner(getPlayer());
|
||||
|
||||
// Force recalc of const boosted skills
|
||||
avatar.recalcConstellations();
|
||||
|
||||
// Add to avatar storage
|
||||
this.avatars.put(avatar.getAvatarId(), avatar);
|
||||
this.avatarsGuid.put(avatar.getGuid(), avatar);
|
||||
}
|
||||
}
|
||||
|
||||
public void postLoad() {
|
||||
for (Avatar avatar : this) {
|
||||
// Weapon check
|
||||
if (avatar.getWeapon() == null) {
|
||||
this.addStartingWeapon(avatar);
|
||||
}
|
||||
// Recalc stats
|
||||
avatar.recalcStats();
|
||||
}
|
||||
}
|
||||
// Set owner first
|
||||
avatar.setOwner(getPlayer());
|
||||
|
||||
@Override
|
||||
public Iterator<Avatar> iterator() {
|
||||
return getAvatars().values().iterator();
|
||||
}
|
||||
// Put into maps
|
||||
this.avatars.put(avatar.getAvatarId(), avatar);
|
||||
this.avatarsGuid.put(avatar.getGuid(), avatar);
|
||||
|
||||
avatar.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addStartingWeapon(Avatar avatar) {
|
||||
// Make sure avatar owner is this player
|
||||
if (avatar.getPlayer() != this.getPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create weapon
|
||||
GameItem weapon = new GameItem(avatar.getAvatarData().getInitialWeapon());
|
||||
|
||||
if (weapon.getItemData() != null) {
|
||||
this.getPlayer().getInventory().addItem(weapon);
|
||||
|
||||
avatar.equipItem(weapon, true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wearFlycloak(long avatarGuid, int flycloakId) {
|
||||
Avatar avatar = this.getAvatarByGuid(avatarGuid);
|
||||
|
||||
if (avatar == null || !getPlayer().getFlyCloakList().contains(flycloakId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
avatar.setFlyCloak(flycloakId);
|
||||
avatar.save();
|
||||
|
||||
// Update
|
||||
getPlayer().sendPacket(new PacketAvatarFlycloakChangeNotify(avatar));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean changeCostume(long avatarGuid, int costumeId) {
|
||||
Avatar avatar = this.getAvatarByGuid(avatarGuid);
|
||||
|
||||
if (avatar == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (costumeId != 0 && !getPlayer().getCostumeList().contains(costumeId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO make sure avatar can wear costume
|
||||
|
||||
avatar.setCostume(costumeId);
|
||||
avatar.save();
|
||||
|
||||
// Update entity
|
||||
EntityAvatar entity = avatar.getAsEntity();
|
||||
if (entity == null) {
|
||||
entity = new EntityAvatar(avatar);
|
||||
getPlayer().sendPacket(new PacketAvatarChangeCostumeNotify(entity));
|
||||
} else {
|
||||
getPlayer().getScene().broadcastPacket(new PacketAvatarChangeCostumeNotify(entity));
|
||||
}
|
||||
|
||||
// Done
|
||||
return true;
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<Avatar> avatars = DatabaseHelper.getAvatars(getPlayer());
|
||||
|
||||
for (Avatar avatar : avatars) {
|
||||
// Should never happen
|
||||
if (avatar.getObjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
|
||||
if (avatarData == null || skillDepot == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set ownerships
|
||||
avatar.setAvatarData(avatarData);
|
||||
avatar.setSkillDepot(skillDepot);
|
||||
avatar.setOwner(getPlayer());
|
||||
|
||||
// Force recalc of const boosted skills
|
||||
avatar.recalcConstellations();
|
||||
|
||||
// Add to avatar storage
|
||||
this.avatars.put(avatar.getAvatarId(), avatar);
|
||||
this.avatarsGuid.put(avatar.getGuid(), avatar);
|
||||
}
|
||||
}
|
||||
|
||||
public void postLoad() {
|
||||
for (Avatar avatar : this) {
|
||||
// Weapon check
|
||||
if (avatar.getWeapon() == null) {
|
||||
this.addStartingWeapon(avatar);
|
||||
}
|
||||
// Recalc stats
|
||||
avatar.recalcStats();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Avatar> iterator() {
|
||||
return getAvatars().values().iterator();
|
||||
}
|
||||
}
|
||||
|
@ -42,322 +42,322 @@ import lombok.Getter;
|
||||
|
||||
@Entity(value = "battlepass", useDiscriminator = false)
|
||||
public class BattlePassManager extends BasePlayerDataManager {
|
||||
@Id @Getter private ObjectId id;
|
||||
|
||||
@Indexed private int ownerUid;
|
||||
@Id @Getter private ObjectId id;
|
||||
|
||||
@Indexed private int ownerUid;
|
||||
@Getter private int point;
|
||||
@Getter private int cyclePoints; // Weekly maximum cap
|
||||
@Getter private int level;
|
||||
|
||||
|
||||
@Getter private boolean viewed;
|
||||
private boolean paid;
|
||||
|
||||
|
||||
private Map<Integer, BattlePassMission> missions;
|
||||
private Map<Integer, BattlePassReward> takenRewards;
|
||||
|
||||
|
||||
@Deprecated // Morphia only
|
||||
public BattlePassManager() {}
|
||||
|
||||
public BattlePassManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.ownerUid = player.getUid();
|
||||
this.player = player;
|
||||
this.ownerUid = player.getUid();
|
||||
}
|
||||
|
||||
|
||||
public void updateViewed() {
|
||||
this.viewed = true;
|
||||
this.viewed = true;
|
||||
}
|
||||
|
||||
public boolean setLevel(int level) {
|
||||
if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
this.level = level;
|
||||
this.point = 0;
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public boolean setLevel(int level) {
|
||||
if (level >= 0 && level <= GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
this.level = level;
|
||||
this.point = 0;
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.player));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPoints(int points){
|
||||
public void addPoints(int points) {
|
||||
this.addPointsDirectly(points, false);
|
||||
|
||||
|
||||
this.player.sendPacket(new PacketBattlePassCurScheduleUpdateNotify(player));
|
||||
this.save();
|
||||
}
|
||||
|
||||
public void addPointsDirectly(int points, boolean isWeekly) {
|
||||
int amount = points;
|
||||
|
||||
if (isWeekly) {
|
||||
amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int amount = points;
|
||||
|
||||
if (isWeekly) {
|
||||
amount = Math.min(amount, GameConstants.BATTLE_PASS_POINT_PER_WEEK - this.cyclePoints);
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.point += amount;
|
||||
this.cyclePoints += amount;
|
||||
|
||||
|
||||
if (this.point >= GameConstants.BATTLE_PASS_POINT_PER_LEVEL && this.getLevel() < GameConstants.BATTLE_PASS_MAX_LEVEL) {
|
||||
int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
|
||||
// Make sure player cant go above max BP level
|
||||
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
|
||||
|
||||
// Set new points after level up
|
||||
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
this.level += levelups;
|
||||
int levelups = Math.floorDiv(this.point, GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
|
||||
// Make sure player cant go above max BP level
|
||||
levelups = Math.min(levelups, GameConstants.BATTLE_PASS_MAX_LEVEL - levelups);
|
||||
|
||||
// Set new points after level up
|
||||
this.point = this.point - (levelups * GameConstants.BATTLE_PASS_POINT_PER_LEVEL);
|
||||
this.level += levelups;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassMission> getMissions() {
|
||||
if (this.missions == null) this.missions = new HashMap<>();
|
||||
return this.missions;
|
||||
}
|
||||
|
||||
// Will return a new empty mission if the mission id is not found
|
||||
public BattlePassMission loadMissionById(int id) {
|
||||
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
|
||||
}
|
||||
|
||||
public boolean hasMission(int id) {
|
||||
return getMissions().containsKey(id);
|
||||
}
|
||||
public Map<Integer, BattlePassMission> getMissions() {
|
||||
if (this.missions == null) this.missions = new HashMap<>();
|
||||
return this.missions;
|
||||
}
|
||||
|
||||
public boolean isPaid() {
|
||||
// ToDo: Change this when we actually support unlocking "paid" BP.
|
||||
return true;
|
||||
}
|
||||
// Will return a new empty mission if the mission id is not found
|
||||
public BattlePassMission loadMissionById(int id) {
|
||||
return getMissions().computeIfAbsent(id, i -> new BattlePassMission(i));
|
||||
}
|
||||
|
||||
public Map<Integer, BattlePassReward> getTakenRewards() {
|
||||
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
|
||||
return this.takenRewards;
|
||||
}
|
||||
|
||||
// Mission triggers
|
||||
public void triggerMission(WatcherTriggerType triggerType) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
|
||||
}
|
||||
|
||||
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType, param, progress);
|
||||
}
|
||||
|
||||
// Handlers
|
||||
public void takeMissionPoint(List<Integer> missionIdList) {
|
||||
// Obvious exploit check
|
||||
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
|
||||
|
||||
for (int id : missionIdList) {
|
||||
// Skip if we dont have this mission
|
||||
if (!this.hasMission(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassMission mission = this.loadMissionById(id);
|
||||
|
||||
if (mission.getData() == null) {
|
||||
this.getMissions().remove(mission.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Take reward
|
||||
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
|
||||
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
|
||||
|
||||
updatedMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedMissions.size() > 0) {
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Packet
|
||||
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
}
|
||||
public boolean hasMission(int id) {
|
||||
return getMissions().containsKey(id);
|
||||
}
|
||||
|
||||
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
|
||||
// Sanity checks.
|
||||
if (rewardItemData.getItemUse().size() < 1) {
|
||||
return;
|
||||
}
|
||||
public boolean isPaid() {
|
||||
// ToDo: Change this when we actually support unlocking "paid" BP.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get possible item choices.
|
||||
String[] choices = rewardItemData.getItemUse().get(0).getUseParam().get(0).split(",");
|
||||
if (choices.length < index) {
|
||||
return;
|
||||
}
|
||||
public Map<Integer, BattlePassReward> getTakenRewards() {
|
||||
if (this.takenRewards == null) this.takenRewards = new HashMap<>();
|
||||
return this.takenRewards;
|
||||
}
|
||||
|
||||
// Get data for the selected item.
|
||||
// This depends on the type of chest.
|
||||
int chosenId = Integer.parseInt(choices[index - 1]);
|
||||
// Mission triggers
|
||||
public void triggerMission(WatcherTriggerType triggerType) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType);
|
||||
}
|
||||
|
||||
// For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's data.
|
||||
if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_ADD_SELECT_ITEM")) {
|
||||
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
|
||||
else if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_GRANT_SELECT_REWARD")) {
|
||||
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
|
||||
public void triggerMission(WatcherTriggerType triggerType, int param, int progress) {
|
||||
getPlayer().getServer().getBattlePassSystem().triggerMission(getPlayer(), triggerType, param, progress);
|
||||
}
|
||||
|
||||
for (var r : selectedReward.getRewardItemList()) {
|
||||
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Grasscutter.getLogger().error("Invalid chest type for BP reward.");
|
||||
}
|
||||
}
|
||||
|
||||
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
|
||||
List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
|
||||
|
||||
for (BattlePassRewardTakeOption option : takeOptionList) {
|
||||
// Duplicate check
|
||||
if (option.getTag().getRewardId() == 0 || getTakenRewards().containsKey(option.getTag().getRewardId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Level check
|
||||
if (option.getTag().getLevel() > this.getLevel()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassRewardData rewardData = GameData.getBattlePassRewardDataMap().get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
|
||||
|
||||
// Sanity check with excel data
|
||||
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else if (this.isPaid() && rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
}
|
||||
else {
|
||||
Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
|
||||
}
|
||||
}
|
||||
|
||||
// Get rewards
|
||||
List<GameItem> rewardItems = null;
|
||||
|
||||
if (rewardList.size() > 0) {
|
||||
// Handlers
|
||||
public void takeMissionPoint(List<Integer> missionIdList) {
|
||||
// Obvious exploit check
|
||||
if (missionIdList.size() > GameData.getBattlePassMissionDataMap().size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rewardItems = new ArrayList<>();
|
||||
|
||||
for (var option : rewardList) {
|
||||
var tag = option.getTag();
|
||||
int index = option.getOptionIdx();
|
||||
List<BattlePassMission> updatedMissions = new ArrayList<>(missionIdList.size());
|
||||
|
||||
// Make sure we have reward data.
|
||||
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
|
||||
if (reward == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add reward items.
|
||||
for (var entry : reward.getRewardItemList()) {
|
||||
ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());
|
||||
for (int id : missionIdList) {
|
||||
// Skip if we dont have this mission
|
||||
if (!this.hasMission(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some rewards are chests where the user can select the item they want.
|
||||
if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
|
||||
this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
|
||||
}
|
||||
// All other rewards directly give us the right item.
|
||||
else {
|
||||
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
BattlePassMission mission = this.loadMissionById(id);
|
||||
|
||||
// Construct the reward and set as taken.
|
||||
BattlePassReward bpReward = new BattlePassReward(tag.getLevel(), tag.getRewardId(), tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
|
||||
this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Add items and send battle pass schedule packet
|
||||
getPlayer().getInventory().addItems(rewardItems);
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
|
||||
}
|
||||
|
||||
public int buyLevels(int buyLevel) {
|
||||
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
|
||||
|
||||
if (boughtLevels > 0) {
|
||||
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
|
||||
|
||||
if (getPlayer().getPrimogems() < price) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.level += boughtLevels;
|
||||
this.save();
|
||||
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
return boughtLevels;
|
||||
}
|
||||
|
||||
public void resetDailyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
if (mission.getData() == null) {
|
||||
this.getMissions().remove(mission.getId());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == null || mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
// Take reward
|
||||
if (mission.getStatus() == BattlePassMissionStatus.MISSION_STATUS_FINISHED) {
|
||||
this.addPointsDirectly(mission.getData().getAddPoint(), mission.getData().isCycleRefresh());
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_POINT_TAKEN);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
updatedMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
public void resetWeeklyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
if (updatedMissions.size() > 0) {
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
// Packet
|
||||
getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(updatedMissions));
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
}
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
|
||||
// Sanity checks.
|
||||
if (rewardItemData.getItemUse().size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
//
|
||||
public BattlePassSchedule getScheduleProto() {
|
||||
var currentDate = LocalDate.now();
|
||||
var nextSundayDate = (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY)
|
||||
? currentDate
|
||||
: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
|
||||
var nextSundayTime = LocalDateTime.of(nextSundayDate.getYear(), nextSundayDate.getMonthValue(), nextSundayDate.getDayOfMonth(), 23, 59, 59);
|
||||
|
||||
BattlePassSchedule.Builder schedule = BattlePassSchedule.newBuilder()
|
||||
// Get possible item choices.
|
||||
String[] choices = rewardItemData.getItemUse().get(0).getUseParam().get(0).split(",");
|
||||
if (choices.length < index) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get data for the selected item.
|
||||
// This depends on the type of chest.
|
||||
int chosenId = Integer.parseInt(choices[index - 1]);
|
||||
|
||||
// For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's data.
|
||||
if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_ADD_SELECT_ITEM")) {
|
||||
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
|
||||
else if (rewardItemData.getItemUse().get(0).getUseOp().equals("ITEM_USE_GRANT_SELECT_REWARD")) {
|
||||
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
|
||||
|
||||
for (var r : selectedReward.getRewardItemList()) {
|
||||
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Grasscutter.getLogger().error("Invalid chest type for BP reward.");
|
||||
}
|
||||
}
|
||||
|
||||
public void takeReward(List<BattlePassRewardTakeOption> takeOptionList) {
|
||||
List<BattlePassRewardTakeOption> rewardList = new ArrayList<>();
|
||||
|
||||
for (BattlePassRewardTakeOption option : takeOptionList) {
|
||||
// Duplicate check
|
||||
if (option.getTag().getRewardId() == 0 || getTakenRewards().containsKey(option.getTag().getRewardId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Level check
|
||||
if (option.getTag().getLevel() > this.getLevel()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BattlePassRewardData rewardData = GameData.getBattlePassRewardDataMap().get(GameConstants.BATTLE_PASS_CURRENT_INDEX * 100 + option.getTag().getLevel());
|
||||
|
||||
// Sanity check with excel data
|
||||
if (rewardData.getFreeRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
} else if (this.isPaid() && rewardData.getPaidRewardIdList().contains(option.getTag().getRewardId())) {
|
||||
rewardList.add(option);
|
||||
}
|
||||
else {
|
||||
Grasscutter.getLogger().info("Not in rewards list: {}", option.getTag().getRewardId());
|
||||
}
|
||||
}
|
||||
|
||||
// Get rewards
|
||||
List<GameItem> rewardItems = null;
|
||||
|
||||
if (rewardList.size() > 0) {
|
||||
|
||||
rewardItems = new ArrayList<>();
|
||||
|
||||
for (var option : rewardList) {
|
||||
var tag = option.getTag();
|
||||
int index = option.getOptionIdx();
|
||||
|
||||
// Make sure we have reward data.
|
||||
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
|
||||
if (reward == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add reward items.
|
||||
for (var entry : reward.getRewardItemList()) {
|
||||
ItemData rewardItemData = GameData.getItemDataMap().get(entry.getItemId());
|
||||
|
||||
// Some rewards are chests where the user can select the item they want.
|
||||
if (rewardItemData.getMaterialType() == MaterialType.MATERIAL_SELECTABLE_CHEST) {
|
||||
this.takeRewardsFromSelectChest(rewardItemData, index, entry, rewardItems);
|
||||
}
|
||||
// All other rewards directly give us the right item.
|
||||
else {
|
||||
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
|
||||
rewardItems.add(rewardItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the reward and set as taken.
|
||||
BattlePassReward bpReward = new BattlePassReward(tag.getLevel(), tag.getRewardId(), tag.getUnlockStatus() == BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID);
|
||||
this.getTakenRewards().put(bpReward.getRewardId(), bpReward);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
this.save();
|
||||
|
||||
// Add items and send battle pass schedule packet
|
||||
getPlayer().getInventory().addItems(rewardItems);
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketTakeBattlePassRewardRsp(takeOptionList, rewardItems));
|
||||
}
|
||||
|
||||
public int buyLevels(int buyLevel) {
|
||||
int boughtLevels = Math.min(buyLevel, GameConstants.BATTLE_PASS_MAX_LEVEL - buyLevel);
|
||||
|
||||
if (boughtLevels > 0) {
|
||||
int price = GameConstants.BATTLE_PASS_LEVEL_PRICE * boughtLevels;
|
||||
|
||||
if (getPlayer().getPrimogems() < price) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.level += boughtLevels;
|
||||
this.save();
|
||||
|
||||
getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(getPlayer()));
|
||||
}
|
||||
|
||||
return boughtLevels;
|
||||
}
|
||||
|
||||
public void resetDailyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == null || mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_DAILY) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
public void resetWeeklyMissions() {
|
||||
var resetMissions = new ArrayList<BattlePassMission>();
|
||||
|
||||
for (var mission : this.missions.values()) {
|
||||
if (mission.getData().getRefreshType() == BattlePassMissionRefreshType.BATTLE_PASS_MISSION_REFRESH_CYCLE_CROSS_SCHEDULE) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_UNFINISHED);
|
||||
mission.setProgress(0);
|
||||
|
||||
resetMissions.add(mission);
|
||||
}
|
||||
}
|
||||
|
||||
this.getPlayer().sendPacket(new PacketBattlePassMissionUpdateNotify(resetMissions));
|
||||
this.getPlayer().sendPacket(new PacketBattlePassCurScheduleUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
//
|
||||
public BattlePassSchedule getScheduleProto() {
|
||||
var currentDate = LocalDate.now();
|
||||
var nextSundayDate = (currentDate.getDayOfWeek() == DayOfWeek.SUNDAY)
|
||||
? currentDate
|
||||
: LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
|
||||
var nextSundayTime = LocalDateTime.of(nextSundayDate.getYear(), nextSundayDate.getMonthValue(), nextSundayDate.getDayOfMonth(), 23, 59, 59);
|
||||
|
||||
BattlePassSchedule.Builder schedule = BattlePassSchedule.newBuilder()
|
||||
.setScheduleId(2700)
|
||||
.setLevel(this.getLevel())
|
||||
.setPoint(this.getPoint())
|
||||
@ -366,21 +366,21 @@ public class BattlePassManager extends BasePlayerDataManager {
|
||||
.setIsViewed(this.isViewed())
|
||||
.setUnlockStatus(this.isPaid() ? BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_PAID : BattlePassUnlockStatus.BATTLE_PASS_UNLOCK_STATUS_FREE)
|
||||
.setJPFMGBEBBBJ(2) // Not bought on Playstation.
|
||||
.setCurCyclePoints(this.getCyclePoints())
|
||||
.setCurCyclePoints(this.getCyclePoints())
|
||||
.setCurCycle(BattlePassCycle.newBuilder()
|
||||
.setBeginTime(0)
|
||||
.setEndTime((int)nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
|
||||
.setCycleIdx(3)
|
||||
);
|
||||
|
||||
for (BattlePassReward reward : getTakenRewards().values()) {
|
||||
schedule.addRewardTakenList(reward.toProto());
|
||||
}
|
||||
|
||||
return schedule.build();
|
||||
}
|
||||
|
||||
.setBeginTime(0)
|
||||
.setEndTime((int)nextSundayTime.atZone(ZoneId.systemDefault()).toEpochSecond())
|
||||
.setCycleIdx(3)
|
||||
);
|
||||
|
||||
for (BattlePassReward reward : getTakenRewards().values()) {
|
||||
schedule.addRewardTakenList(reward.toProto());
|
||||
}
|
||||
|
||||
return schedule.build();
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveBattlePass(this);
|
||||
DatabaseHelper.saveBattlePass(this);
|
||||
}
|
||||
}
|
||||
|
@ -16,64 +16,64 @@ import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketBattlePassMissionUpdateNotify;
|
||||
|
||||
public class BattlePassSystem extends BaseGameSystem {
|
||||
private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers;
|
||||
|
||||
// BP Mission manager for the server, contains cached triggers so we dont have to load it for each player
|
||||
public BattlePassSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
this.cachedTriggers = new HashMap<>();
|
||||
|
||||
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
|
||||
if (missionData.isValidRefreshType()) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
|
||||
triggerList.add(missionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
private final Map<WatcherTriggerType, List<BattlePassMissionData>> cachedTriggers;
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
|
||||
return cachedTriggers;
|
||||
}
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType) {
|
||||
triggerMission(player, triggerType, 0, 1);
|
||||
}
|
||||
// BP Mission manager for the server, contains cached triggers so we dont have to load it for each player
|
||||
public BattlePassSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType, int param, int progress) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
|
||||
|
||||
if (triggerList == null || triggerList.isEmpty()) return;
|
||||
|
||||
for (BattlePassMissionData data : triggerList) {
|
||||
// Skip params check if param == 0
|
||||
if (param != 0) {
|
||||
if (!data.getMainParams().contains(param)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get mission from player, if it doesnt exist, then we make one
|
||||
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
|
||||
|
||||
if (mission.isFinshed()) continue;
|
||||
this.cachedTriggers = new HashMap<>();
|
||||
|
||||
// Add progress
|
||||
mission.addProgress(progress, data.getProgress());
|
||||
|
||||
if (mission.getProgress() >= data.getProgress()) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
player.getBattlePassManager().save();
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
|
||||
}
|
||||
}
|
||||
for (BattlePassMissionData missionData : GameData.getBattlePassMissionDataMap().values()) {
|
||||
if (missionData.isValidRefreshType()) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().computeIfAbsent(missionData.getTriggerType(), e -> new ArrayList<>());
|
||||
triggerList.add(missionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private Map<WatcherTriggerType, List<BattlePassMissionData>> getTriggers() {
|
||||
return cachedTriggers;
|
||||
}
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType) {
|
||||
triggerMission(player, triggerType, 0, 1);
|
||||
}
|
||||
|
||||
public void triggerMission(Player player, WatcherTriggerType triggerType, int param, int progress) {
|
||||
List<BattlePassMissionData> triggerList = getTriggers().get(triggerType);
|
||||
|
||||
if (triggerList == null || triggerList.isEmpty()) return;
|
||||
|
||||
for (BattlePassMissionData data : triggerList) {
|
||||
// Skip params check if param == 0
|
||||
if (param != 0) {
|
||||
if (!data.getMainParams().contains(param)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get mission from player, if it doesnt exist, then we make one
|
||||
BattlePassMission mission = player.getBattlePassManager().loadMissionById(data.getId());
|
||||
|
||||
if (mission.isFinshed()) continue;
|
||||
|
||||
// Add progress
|
||||
mission.addProgress(progress, data.getProgress());
|
||||
|
||||
if (mission.getProgress() >= data.getProgress()) {
|
||||
mission.setStatus(BattlePassMissionStatus.MISSION_STATUS_FINISHED);
|
||||
}
|
||||
|
||||
// Save to db
|
||||
player.getBattlePassManager().save();
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketBattlePassMissionUpdateNotify(mission));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,59 +31,59 @@ import java.util.List;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
public class CombineManger extends BaseGameSystem {
|
||||
private final static Int2ObjectMap<List<Integer>> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>();
|
||||
private final static Int2ObjectMap<List<Integer>> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public CombineManger(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Read the data we need for strongbox.
|
||||
try (Reader fileReader = DataLoader.loadReader("ReliquaryDecompose.json")) {
|
||||
List<ReliquaryDecomposeEntry> decomposeEntries = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ReliquaryDecomposeEntry.class).getType());
|
||||
// Read the data we need for strongbox.
|
||||
try (Reader fileReader = DataLoader.loadReader("ReliquaryDecompose.json")) {
|
||||
List<ReliquaryDecomposeEntry> decomposeEntries = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ReliquaryDecomposeEntry.class).getType());
|
||||
|
||||
for (ReliquaryDecomposeEntry entry : decomposeEntries) {
|
||||
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
|
||||
}
|
||||
for (ReliquaryDecomposeEntry entry : decomposeEntries) {
|
||||
reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems());
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex);
|
||||
}
|
||||
}
|
||||
Grasscutter.getLogger().debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlockCombineDiagram(Player player, GameItem diagramItem) {
|
||||
// Make sure this is actually a diagram.
|
||||
if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
|
||||
return false;
|
||||
}
|
||||
// Make sure this is actually a diagram.
|
||||
if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine the combine item we should unlock.
|
||||
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam().get(0));
|
||||
// Determine the combine item we should unlock.
|
||||
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam().get(0));
|
||||
|
||||
// Remove the diagram from the player's inventory.
|
||||
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
|
||||
// update when unlocking the diagram.
|
||||
player.getInventory().removeItem(diagramItem, 1);
|
||||
// Remove the diagram from the player's inventory.
|
||||
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
|
||||
// update when unlocking the diagram.
|
||||
player.getInventory().removeItem(diagramItem, 1);
|
||||
|
||||
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||
player.getUnlockedCombines().add(combineId);
|
||||
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||
player.getUnlockedCombines().add(combineId);
|
||||
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public CombineResult combineItem(Player player, int cid, int count){
|
||||
public CombineResult combineItem(Player player, int cid, int count) {
|
||||
// check config exist
|
||||
if(!GameData.getCombineDataMap().containsKey(cid)){
|
||||
if (!GameData.getCombineDataMap().containsKey(cid)) {
|
||||
player.getWorld().getHost().sendPacket(new PacketCombineRsp());
|
||||
return null;
|
||||
}
|
||||
|
||||
CombineData combineData = GameData.getCombineDataMap().get(cid);
|
||||
|
||||
if(combineData.getPlayerLevel() > player.getLevel()){
|
||||
if (combineData.getPlayerLevel() > player.getLevel()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ public class CombineManger extends BaseGameSystem {
|
||||
player.sendPacket(new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check if the number of input items matches the output count.
|
||||
if (input.size() != count * 3) {
|
||||
player.sendPacket(new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR));
|
||||
@ -144,7 +144,7 @@ public class CombineManger extends BaseGameSystem {
|
||||
List<Long> resultItems = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
int itemId = Utils.drawRandomListElement(possibleDrops);
|
||||
GameItem newReliquary = new GameItem(itemId, 1);
|
||||
GameItem newReliquary = new GameItem(itemId, 1);
|
||||
|
||||
player.getInventory().addItem(newReliquary);
|
||||
resultItems.add(newReliquary.getGuid());
|
||||
|
@ -32,7 +32,7 @@ public class DropSystem extends BaseGameSystem {
|
||||
this.dropData = new Int2ObjectOpenHashMap<>();
|
||||
this.load();
|
||||
}
|
||||
|
||||
|
||||
public Int2ObjectMap<List<DropData>> getDropData() {
|
||||
return dropData;
|
||||
}
|
||||
@ -41,7 +41,7 @@ public class DropSystem extends BaseGameSystem {
|
||||
try (Reader fileReader = DataLoader.loadReader("Drop.json")) {
|
||||
getDropData().clear();
|
||||
List<DropInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType());
|
||||
if(banners.size() > 0) {
|
||||
if (banners.size() > 0) {
|
||||
for (DropInfo di : banners) {
|
||||
getDropData().put(di.getMonsterId(), di.getDropDataList());
|
||||
}
|
||||
|
@ -20,95 +20,95 @@ import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonSystem extends BaseGameSystem {
|
||||
private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
|
||||
|
||||
public DungeonSystem(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
private static final BasicDungeonSettleListener basicDungeonSettleObserver = new BasicDungeonSettleListener();
|
||||
|
||||
public void getEntryInfo(Player player, int pointId) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
|
||||
|
||||
if (entry == null) {
|
||||
// Error
|
||||
player.sendPacket(new PacketDungeonEntryInfoRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
|
||||
}
|
||||
public DungeonSystem(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
Grasscutter.getLogger().info("{}({}) is trying to enter dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
|
||||
|
||||
int sceneId = data.getSceneId();
|
||||
player.getScene().setPrevScene(sceneId);
|
||||
|
||||
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
|
||||
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
|
||||
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
||||
}
|
||||
|
||||
player.getScene().setPrevScenePoint(pointId);
|
||||
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
|
||||
return true;
|
||||
}
|
||||
public void getEntryInfo(Player player, int pointId) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
|
||||
|
||||
/**
|
||||
* used in tower dungeons handoff
|
||||
*/
|
||||
public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
if (entry == null) {
|
||||
// Error
|
||||
player.sendPacket(new PacketDungeonEntryInfoRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
Grasscutter.getLogger().info("{}({}) is trying to enter tower dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
|
||||
player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData()));
|
||||
}
|
||||
|
||||
if(player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)){
|
||||
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
|
||||
public void exitDungeon(Player player) {
|
||||
Scene scene = player.getScene();
|
||||
|
||||
if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get previous scene
|
||||
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
||||
|
||||
// Get previous position
|
||||
DungeonData dungeonData = scene.getDungeonData();
|
||||
Position prevPos = new Position(GameConstants.START_POSITION);
|
||||
|
||||
if (dungeonData != null) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
||||
|
||||
if (entry != null) {
|
||||
prevPos.set(entry.getPointData().getTranPos());
|
||||
}
|
||||
}
|
||||
// clean temp team if it has
|
||||
player.getTeamManager().cleanTemporaryTeam();
|
||||
player.getTowerManager().clearEntry();
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
Grasscutter.getLogger().info("{}({}) is trying to enter dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
|
||||
|
||||
// Transfer player back to world
|
||||
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
||||
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
|
||||
}
|
||||
|
||||
public void updateDailyDungeons() {
|
||||
for (ScenePointEntry entry : GameData.getScenePointEntries().values()) {
|
||||
entry.getPointData().updateDailyDungeon();
|
||||
}
|
||||
}
|
||||
int sceneId = data.getSceneId();
|
||||
player.getScene().setPrevScene(sceneId);
|
||||
|
||||
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
|
||||
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
|
||||
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
||||
}
|
||||
|
||||
player.getScene().setPrevScenePoint(pointId);
|
||||
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* used in tower dungeons handoff
|
||||
*/
|
||||
public boolean handoffDungeon(Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
Grasscutter.getLogger().info("{}({}) is trying to enter tower dungeon {}" ,player.getNickname(),player.getUid(),dungeonId);
|
||||
|
||||
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
||||
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void exitDungeon(Player player) {
|
||||
Scene scene = player.getScene();
|
||||
|
||||
if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get previous scene
|
||||
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
||||
|
||||
// Get previous position
|
||||
DungeonData dungeonData = scene.getDungeonData();
|
||||
Position prevPos = new Position(GameConstants.START_POSITION);
|
||||
|
||||
if (dungeonData != null) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
||||
|
||||
if (entry != null) {
|
||||
prevPos.set(entry.getPointData().getTranPos());
|
||||
}
|
||||
}
|
||||
// clean temp team if it has
|
||||
player.getTeamManager().cleanTemporaryTeam();
|
||||
player.getTowerManager().clearEntry();
|
||||
|
||||
// Transfer player back to world
|
||||
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
||||
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
|
||||
}
|
||||
|
||||
public void updateDailyDungeons() {
|
||||
for (ScenePointEntry entry : GameData.getScenePointEntries().values()) {
|
||||
entry.getPointData().updateDailyDungeon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,180 +36,180 @@ import com.google.gson.reflect.TypeToken;
|
||||
|
||||
public class DungeonChallenge extends WorldChallenge {
|
||||
|
||||
/**
|
||||
* has more challenge
|
||||
*/
|
||||
private boolean stage;
|
||||
private IntSet rewardedPlayers;
|
||||
/**
|
||||
* has more challenge
|
||||
*/
|
||||
private boolean stage;
|
||||
private IntSet rewardedPlayers;
|
||||
|
||||
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
|
||||
private final static Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static void initialize() {
|
||||
// Read the data we need for dungeon rewards drops.
|
||||
try (Reader fileReader = DataLoader.loadReader("DungeonDrop.json")) {
|
||||
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
|
||||
public static void initialize() {
|
||||
// Read the data we need for dungeon rewards drops.
|
||||
try (Reader fileReader = DataLoader.loadReader("DungeonDrop.json")) {
|
||||
List<DungeonDrop> dungeonDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DungeonDrop.class).getType());
|
||||
|
||||
for (DungeonDrop entry : dungeonDropList) {
|
||||
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
|
||||
}
|
||||
for (DungeonDrop entry : dungeonDropList) {
|
||||
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Loaded {} dungeon drop data entries.", dungeonDropData.size());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
|
||||
}
|
||||
}
|
||||
Grasscutter.getLogger().debug("Loaded {} dungeon drop data entries.", dungeonDropData.size());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||
int challengeId, int challengeIndex,
|
||||
List<Integer> paramList,
|
||||
int timeLimit, int goal,
|
||||
List<ChallengeTrigger> challengeTriggers) {
|
||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||
this.setRewardedPlayers(new IntOpenHashSet());
|
||||
}
|
||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||
int challengeId, int challengeIndex,
|
||||
List<Integer> paramList,
|
||||
int timeLimit, int goal,
|
||||
List<ChallengeTrigger> challengeTriggers) {
|
||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||
this.setRewardedPlayers(new IntOpenHashSet());
|
||||
}
|
||||
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public IntSet getRewardedPlayers() {
|
||||
return rewardedPlayers;
|
||||
}
|
||||
public IntSet getRewardedPlayers() {
|
||||
return rewardedPlayers;
|
||||
}
|
||||
|
||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||
this.rewardedPlayers = rewardedPlayers;
|
||||
}
|
||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||
this.rewardedPlayers = rewardedPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
super.done();
|
||||
if (this.isSuccess()) {
|
||||
// Settle
|
||||
settle();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void done() {
|
||||
super.done();
|
||||
if (this.isSuccess()) {
|
||||
// Settle
|
||||
settle();
|
||||
}
|
||||
}
|
||||
|
||||
private void settle() {
|
||||
if(!stage){
|
||||
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
||||
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||
// Battle pass trigger
|
||||
this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
|
||||
}
|
||||
}
|
||||
private void settle() {
|
||||
if (!stage) {
|
||||
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
||||
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||
// Battle pass trigger
|
||||
this.getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
|
||||
}
|
||||
}
|
||||
|
||||
private List<GameItem> rollRewards(boolean useCondensed) {
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
int dungeonId = this.getScene().getDungeonData().getId();
|
||||
// If we have specific drop data for this dungeon, we use it.
|
||||
if (dungeonDropData.containsKey(dungeonId)) {
|
||||
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
|
||||
private List<GameItem> rollRewards(boolean useCondensed) {
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
int dungeonId = this.getScene().getDungeonData().getId();
|
||||
// If we have specific drop data for this dungeon, we use it.
|
||||
if (dungeonDropData.containsKey(dungeonId)) {
|
||||
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
|
||||
|
||||
// Roll for each drop group.
|
||||
for (var entry : dropEntries) {
|
||||
// Determine the number of drops we get for this entry.
|
||||
int start = entry.getCounts().get(0);
|
||||
int end = entry.getCounts().get(entry.getCounts().size() - 1);
|
||||
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
|
||||
// Roll for each drop group.
|
||||
for (var entry : dropEntries) {
|
||||
// Determine the number of drops we get for this entry.
|
||||
int start = entry.getCounts().get(0);
|
||||
int end = entry.getCounts().get(entry.getCounts().size() - 1);
|
||||
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
|
||||
|
||||
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||
|
||||
if (useCondensed) {
|
||||
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||
}
|
||||
if (useCondensed) {
|
||||
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||
}
|
||||
|
||||
// Double rewards in multiplay mode, if specified.
|
||||
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
|
||||
amount *= 2;
|
||||
}
|
||||
// Double rewards in multiplay mode, if specified.
|
||||
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
|
||||
amount *= 2;
|
||||
}
|
||||
|
||||
// Roll items for this group.
|
||||
// Here, we have to handle stacking, or the client will not display results correctly.
|
||||
// For now, we use the following logic: If the possible drop item are a list of multiple items,
|
||||
// we roll them separately. If not, we stack them. This should work out in practice, at least
|
||||
// for the currently existing set of dungeons.
|
||||
if (entry.getItems().size() == 1) {
|
||||
rewards.add(new GameItem(entry.getItems().get(0), amount));
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < amount; i++) {
|
||||
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
|
||||
// int itemId = entry.getItems().get(itemIndex);
|
||||
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
|
||||
rewards.add(new GameItem(itemId, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, we fall back to the preview data.
|
||||
else {
|
||||
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
|
||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
}
|
||||
// Roll items for this group.
|
||||
// Here, we have to handle stacking, or the client will not display results correctly.
|
||||
// For now, we use the following logic: If the possible drop item are a list of multiple items,
|
||||
// we roll them separately. If not, we stack them. This should work out in practice, at least
|
||||
// for the currently existing set of dungeons.
|
||||
if (entry.getItems().size() == 1) {
|
||||
rewards.add(new GameItem(entry.getItems().get(0), amount));
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < amount; i++) {
|
||||
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
|
||||
// int itemId = entry.getItems().get(itemIndex);
|
||||
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
|
||||
rewards.add(new GameItem(itemId, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, we fall back to the preview data.
|
||||
else {
|
||||
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
|
||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||
DungeonData dungeonData = getScene().getDungeonData();
|
||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||
DungeonData dungeonData = getScene().getDungeonData();
|
||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||
|
||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already rewarded
|
||||
if (getRewardedPlayers().contains(player.getUid())) {
|
||||
return;
|
||||
}
|
||||
// Already rewarded
|
||||
if (getRewardedPlayers().contains(player.getUid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rewards.
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
// Get rewards.
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
|
||||
if (request.getIsUseCondenseResin()) {
|
||||
// Check if condensed resin is usable here.
|
||||
// For this, we use the following logic for now:
|
||||
// The normal resin cost of the dungeon has to be 20.
|
||||
if (resinCost != 20) {
|
||||
return;
|
||||
}
|
||||
if (request.getIsUseCondenseResin()) {
|
||||
// Check if condensed resin is usable here.
|
||||
// For this, we use the following logic for now:
|
||||
// The normal resin cost of the dungeon has to be 20.
|
||||
if (resinCost != 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the player has condensed resin.
|
||||
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
||||
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
// Make sure the player has condensed resin.
|
||||
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
||||
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduct.
|
||||
player.getInventory().removeItem(condensedResin, 1);
|
||||
// Deduct.
|
||||
player.getInventory().removeItem(condensedResin, 1);
|
||||
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(true));
|
||||
}
|
||||
else {
|
||||
// If the player used regular resin, try to deduct.
|
||||
// Stop if insufficient resin.
|
||||
boolean success = player.getResinManager().useResin(resinCost);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(true));
|
||||
}
|
||||
else {
|
||||
// If the player used regular resin, try to deduct.
|
||||
// Stop if insufficient resin.
|
||||
boolean success = player.getResinManager().useResin(resinCost);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(false));
|
||||
}
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(false));
|
||||
}
|
||||
|
||||
// Add rewards to player and send notification.
|
||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
// Add rewards to player and send notification.
|
||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
getRewardedPlayers().add(player.getUid());
|
||||
}
|
||||
getRewardedPlayers().add(player.getUid());
|
||||
}
|
||||
}
|
||||
|
@ -40,278 +40,278 @@ import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
|
||||
public class EntityAvatar extends GameEntity {
|
||||
private final Avatar avatar;
|
||||
|
||||
private PlayerDieType killedType;
|
||||
private int killedBy;
|
||||
|
||||
public EntityAvatar(Scene scene, Avatar avatar) {
|
||||
super(scene);
|
||||
this.avatar = avatar;
|
||||
this.avatar.setCurrentEnergy();
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
|
||||
|
||||
GameItem weapon = this.getAvatar().getWeapon();
|
||||
if (weapon != null) {
|
||||
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
||||
}
|
||||
}
|
||||
|
||||
public EntityAvatar(Avatar avatar) {
|
||||
super(null);
|
||||
this.avatar = avatar;
|
||||
this.avatar.setCurrentEnergy();
|
||||
}
|
||||
private final Avatar avatar;
|
||||
|
||||
public Player getPlayer() {
|
||||
return avatar.getPlayer();
|
||||
}
|
||||
private PlayerDieType killedType;
|
||||
private int killedBy;
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return getPlayer().getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return getPlayer().getRotation();
|
||||
}
|
||||
public EntityAvatar(Scene scene, Avatar avatar) {
|
||||
super(scene);
|
||||
this.avatar = avatar;
|
||||
this.avatar.setCurrentEnergy();
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
|
||||
|
||||
public Avatar getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public int getKilledBy() {
|
||||
return killedBy;
|
||||
}
|
||||
|
||||
public PlayerDieType getKilledType() {
|
||||
return killedType;
|
||||
}
|
||||
GameItem weapon = this.getAvatar().getWeapon();
|
||||
if (weapon != null) {
|
||||
weapon.setWeaponEntityId(getScene().getWorld().getNextEntityId(EntityIdType.WEAPON));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return getAvatar().getFightProperties();
|
||||
}
|
||||
|
||||
public int getWeaponEntityId() {
|
||||
if (getAvatar().getWeapon() != null) {
|
||||
return getAvatar().getWeapon().getWeaponEntityId();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
public EntityAvatar(Avatar avatar) {
|
||||
super(null);
|
||||
this.avatar = avatar;
|
||||
this.avatar.setCurrentEnergy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
||||
this.killedBy = killerId;
|
||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||
}
|
||||
public Player getPlayer() {
|
||||
return avatar.getPlayer();
|
||||
}
|
||||
|
||||
public void onDeath(PlayerDieType dieType, int killerId) {
|
||||
this.killedType = dieType;
|
||||
this.killedBy = killerId;
|
||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float heal(float amount) {
|
||||
float healed = super.heal(amount);
|
||||
|
||||
if (healed > 0f) {
|
||||
getScene().broadcastPacket(
|
||||
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)
|
||||
);
|
||||
}
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void clearEnergy(ChangeEnergyReason reason) {
|
||||
// Fight props.
|
||||
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return getPlayer().getPosition();
|
||||
}
|
||||
|
||||
// Get max energy.
|
||||
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return getPlayer().getRotation();
|
||||
}
|
||||
|
||||
// Set energy to zero.
|
||||
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
||||
public Avatar getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
// Send packets.
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
||||
public int getKilledBy() {
|
||||
return killedBy;
|
||||
}
|
||||
|
||||
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
|
||||
}
|
||||
}
|
||||
|
||||
public void addEnergy(float amount, PropChangeReason reason) {
|
||||
this.addEnergy(amount, reason, false);
|
||||
}
|
||||
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
|
||||
// Get current and maximum energy for this avatar.
|
||||
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||
public PlayerDieType getKilledType() {
|
||||
return killedType;
|
||||
}
|
||||
|
||||
float curEnergy = this.getFightProperty(curEnergyProp);
|
||||
float maxEnergy = this.getFightProperty(maxEnergyProp);
|
||||
|
||||
// Get energy recharge.
|
||||
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
||||
}
|
||||
|
||||
// Scale amount by energy recharge, if the amount is not flat.
|
||||
if (!isFlat) {
|
||||
amount *= energyRecharge;
|
||||
}
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return getAvatar().getFightProperties();
|
||||
}
|
||||
|
||||
// Determine the new energy value.
|
||||
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
|
||||
|
||||
// Set energy and notify.
|
||||
if (newEnergy != curEnergy) {
|
||||
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
|
||||
|
||||
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
|
||||
}
|
||||
}
|
||||
|
||||
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
||||
.setUid(this.getPlayer().getUid())
|
||||
.setAvatarId(this.getAvatar().getAvatarId())
|
||||
.setGuid(this.getAvatar().getGuid())
|
||||
.setPeerId(this.getPlayer().getPeerId())
|
||||
.addAllTalentIdList(this.getAvatar().getTalentIdList())
|
||||
.setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel())
|
||||
.putAllSkillLevelMap(this.getAvatar().getSkillLevelMap())
|
||||
.setSkillDepotId(this.getAvatar().getSkillDepotId())
|
||||
.addAllInherentProudSkillList(this.getAvatar().getProudSkillList())
|
||||
.putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap())
|
||||
.addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances())
|
||||
.setWearingFlycloakId(this.getAvatar().getFlyCloak())
|
||||
.setCostumeId(this.getAvatar().getCostume())
|
||||
.setBornTime(this.getAvatar().getBornTime());
|
||||
|
||||
for (GameItem item : avatar.getEquips().values()) {
|
||||
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
|
||||
avatarInfo.setWeapon(item.createSceneWeaponInfo());
|
||||
} else {
|
||||
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
|
||||
}
|
||||
avatarInfo.addEquipIdList(item.getItemId());
|
||||
}
|
||||
|
||||
return avatarInfo.build();
|
||||
}
|
||||
public int getWeaponEntityId() {
|
||||
if (getAvatar().getWeapon() != null) {
|
||||
return getAvatar().getWeapon().getWeaponEntityId();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
|
||||
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
|
||||
.setLifeState(this.getLifeState().getValue());
|
||||
|
||||
if (this.getScene() != null) {
|
||||
entityInfo.setMotionInfo(this.getMotionInfo());
|
||||
}
|
||||
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
entityInfo.setAvatar(this.getSceneAvatarInfo());
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
|
||||
public AbilityControlBlock getAbilityControlBlock() {
|
||||
AvatarData data = this.getAvatar().getAvatarData();
|
||||
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
|
||||
int embryoId = 0;
|
||||
|
||||
// Add avatar abilities
|
||||
if (data.getAbilities() != null) {
|
||||
for (int id : data.getAbilities()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
// Add default abilities
|
||||
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
// Add team resonances
|
||||
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
// Add skill depot abilities
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
|
||||
if (skillDepot != null && skillDepot.getAbilities() != null) {
|
||||
for (int id : skillDepot.getAbilities()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
// Add equip abilities
|
||||
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
|
||||
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(Utils.abilityHash(skill))
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return abilityControlBlock.build();
|
||||
}
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER;
|
||||
this.killedBy = killerId;
|
||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||
}
|
||||
|
||||
public void onDeath(PlayerDieType dieType, int killerId) {
|
||||
this.killedType = dieType;
|
||||
this.killedBy = killerId;
|
||||
clearEnergy(ChangeEnergyReason.CHANGE_ENERGY_REASON_NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float heal(float amount) {
|
||||
float healed = super.heal(amount);
|
||||
|
||||
if (healed > 0f) {
|
||||
getScene().broadcastPacket(
|
||||
new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_ADD_ABILITY)
|
||||
);
|
||||
}
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void clearEnergy(ChangeEnergyReason reason) {
|
||||
// Fight props.
|
||||
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||
|
||||
// Get max energy.
|
||||
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
|
||||
|
||||
// Set energy to zero.
|
||||
this.avatar.setCurrentEnergy(curEnergyProp, 0);
|
||||
|
||||
// Send packets.
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
|
||||
|
||||
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason));
|
||||
}
|
||||
}
|
||||
|
||||
public void addEnergy(float amount, PropChangeReason reason) {
|
||||
this.addEnergy(amount, reason, false);
|
||||
}
|
||||
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
|
||||
// Get current and maximum energy for this avatar.
|
||||
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
|
||||
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp();
|
||||
|
||||
float curEnergy = this.getFightProperty(curEnergyProp);
|
||||
float maxEnergy = this.getFightProperty(maxEnergyProp);
|
||||
|
||||
// Get energy recharge.
|
||||
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
|
||||
|
||||
// Scale amount by energy recharge, if the amount is not flat.
|
||||
if (!isFlat) {
|
||||
amount *= energyRecharge;
|
||||
}
|
||||
|
||||
// Determine the new energy value.
|
||||
float newEnergy = Math.min(curEnergy + amount, maxEnergy);
|
||||
|
||||
// Set energy and notify.
|
||||
if (newEnergy != curEnergy) {
|
||||
this.avatar.setCurrentEnergy(curEnergyProp, newEnergy);
|
||||
|
||||
this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp));
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason));
|
||||
}
|
||||
}
|
||||
|
||||
public SceneAvatarInfo getSceneAvatarInfo() {
|
||||
SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder()
|
||||
.setUid(this.getPlayer().getUid())
|
||||
.setAvatarId(this.getAvatar().getAvatarId())
|
||||
.setGuid(this.getAvatar().getGuid())
|
||||
.setPeerId(this.getPlayer().getPeerId())
|
||||
.addAllTalentIdList(this.getAvatar().getTalentIdList())
|
||||
.setCoreProudSkillLevel(this.getAvatar().getCoreProudSkillLevel())
|
||||
.putAllSkillLevelMap(this.getAvatar().getSkillLevelMap())
|
||||
.setSkillDepotId(this.getAvatar().getSkillDepotId())
|
||||
.addAllInherentProudSkillList(this.getAvatar().getProudSkillList())
|
||||
.putAllProudSkillExtraLevelMap(this.getAvatar().getProudSkillBonusMap())
|
||||
.addAllTeamResonanceList(this.getAvatar().getPlayer().getTeamManager().getTeamResonances())
|
||||
.setWearingFlycloakId(this.getAvatar().getFlyCloak())
|
||||
.setCostumeId(this.getAvatar().getCostume())
|
||||
.setBornTime(this.getAvatar().getBornTime());
|
||||
|
||||
for (GameItem item : avatar.getEquips().values()) {
|
||||
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
|
||||
avatarInfo.setWeapon(item.createSceneWeaponInfo());
|
||||
} else {
|
||||
avatarInfo.addReliquaryList(item.createSceneReliquaryInfo());
|
||||
}
|
||||
avatarInfo.addEquipIdList(item.getItemId());
|
||||
}
|
||||
|
||||
return avatarInfo.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR)
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLastMoveSceneTimeMs(this.getLastMoveSceneTimeMs())
|
||||
.setLastMoveReliableSeq(this.getLastMoveReliableSeq())
|
||||
.setLifeState(this.getLifeState().getValue());
|
||||
|
||||
if (this.getScene() != null) {
|
||||
entityInfo.setMotionInfo(this.getMotionInfo());
|
||||
}
|
||||
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getAvatar().getLevel()))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
entityInfo.setAvatar(this.getSceneAvatarInfo());
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
|
||||
public AbilityControlBlock getAbilityControlBlock() {
|
||||
AvatarData data = this.getAvatar().getAvatarData();
|
||||
AbilityControlBlock.Builder abilityControlBlock = AbilityControlBlock.newBuilder();
|
||||
int embryoId = 0;
|
||||
|
||||
// Add avatar abilities
|
||||
if (data.getAbilities() != null) {
|
||||
for (int id : data.getAbilities()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
// Add default abilities
|
||||
for (int id : GameConstants.DEFAULT_ABILITY_HASHES) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
// Add team resonances
|
||||
for (int id : this.getPlayer().getTeamManager().getTeamResonancesConfig()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
// Add skill depot abilities
|
||||
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.getAvatar().getSkillDepotId());
|
||||
if (skillDepot != null && skillDepot.getAbilities() != null) {
|
||||
for (int id : skillDepot.getAbilities()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(id)
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
// Add equip abilities
|
||||
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
|
||||
for (String skill : this.getAvatar().getExtraAbilityEmbryos()) {
|
||||
AbilityEmbryo emb = AbilityEmbryo.newBuilder()
|
||||
.setAbilityId(++embryoId)
|
||||
.setAbilityNameHash(Utils.abilityHash(skill))
|
||||
.setAbilityOverrideNameHash(GameConstants.DEFAULT_ABILITY_NAME)
|
||||
.build();
|
||||
abilityControlBlock.addAbilityEmbryoList(emb);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
return abilityControlBlock.build();
|
||||
}
|
||||
}
|
||||
|
@ -38,125 +38,125 @@ import lombok.ToString;
|
||||
|
||||
@ToString(callSuper = true)
|
||||
public class EntityGadget extends EntityBaseGadget {
|
||||
private final GadgetData data;
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
private int gadgetId;
|
||||
private final GadgetData data;
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
private int gadgetId;
|
||||
|
||||
private int state;
|
||||
private int pointType;
|
||||
private GadgetContent content;
|
||||
private Int2FloatOpenHashMap fightProp;
|
||||
private SceneGadget metaGadget;
|
||||
private int state;
|
||||
private int pointType;
|
||||
private GadgetContent content;
|
||||
private Int2FloatOpenHashMap fightProp;
|
||||
private SceneGadget metaGadget;
|
||||
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
|
||||
super(scene);
|
||||
this.data = GameData.getGadgetDataMap().get(gadgetId);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.gadgetId = gadgetId;
|
||||
this.pos = pos.clone();
|
||||
this.rot = rot != null ? rot.clone() : new Position();
|
||||
}
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
|
||||
super(scene);
|
||||
this.data = GameData.getGadgetDataMap().get(gadgetId);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.gadgetId = gadgetId;
|
||||
this.pos = pos.clone();
|
||||
this.rot = rot != null ? rot.clone() : new Position();
|
||||
}
|
||||
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos) {
|
||||
this(scene, gadgetId, pos, new Position());
|
||||
}
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos) {
|
||||
this(scene, gadgetId, pos, new Position());
|
||||
}
|
||||
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
|
||||
this(scene, gadgetId, pos, rot);
|
||||
this.content = content;
|
||||
}
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
|
||||
this(scene, gadgetId, pos, rot);
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public GadgetData getGadgetData() {
|
||||
return data;
|
||||
}
|
||||
public GadgetData getGadgetData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
|
||||
public void setGadgetId(int gadgetId) {
|
||||
this.gadgetId = gadgetId;
|
||||
}
|
||||
public void setGadgetId(int gadgetId) {
|
||||
this.gadgetId = gadgetId;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
public int getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void updateState(int state){
|
||||
this.setState(state);
|
||||
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
|
||||
}
|
||||
public void updateState(int state) {
|
||||
this.setState(state);
|
||||
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
|
||||
}
|
||||
|
||||
public int getPointType() {
|
||||
return pointType;
|
||||
}
|
||||
public int getPointType() {
|
||||
return pointType;
|
||||
}
|
||||
|
||||
public void setPointType(int pointType) {
|
||||
this.pointType = pointType;
|
||||
}
|
||||
public void setPointType(int pointType) {
|
||||
this.pointType = pointType;
|
||||
}
|
||||
|
||||
public GadgetContent getContent() {
|
||||
return content;
|
||||
}
|
||||
public GadgetContent getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Deprecated // Dont use!
|
||||
public void setContent(GadgetContent content) {
|
||||
this.content = this.content == null ? content : this.content;
|
||||
}
|
||||
@Deprecated // Dont use!
|
||||
public void setContent(GadgetContent content) {
|
||||
this.content = this.content == null ? content : this.content;
|
||||
}
|
||||
|
||||
public SceneGadget getMetaGadget() {
|
||||
return metaGadget;
|
||||
}
|
||||
public SceneGadget getMetaGadget() {
|
||||
return metaGadget;
|
||||
}
|
||||
|
||||
public void setMetaGadget(SceneGadget metaGadget) {
|
||||
this.metaGadget = metaGadget;
|
||||
}
|
||||
public void setMetaGadget(SceneGadget metaGadget) {
|
||||
this.metaGadget = metaGadget;
|
||||
}
|
||||
|
||||
// TODO refactor
|
||||
public void buildContent() {
|
||||
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
|
||||
return;
|
||||
}
|
||||
// TODO refactor
|
||||
public void buildContent() {
|
||||
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityType type = getGadgetData().getType();
|
||||
GadgetContent content = switch (type) {
|
||||
case GatherPoint -> new GadgetGatherPoint(this);
|
||||
case GatherObject -> new GadgetGatherObject(this);
|
||||
case Worktop -> new GadgetWorktop(this);
|
||||
case RewardStatue -> new GadgetRewardStatue(this);
|
||||
case Chest -> new GadgetChest(this);
|
||||
EntityType type = getGadgetData().getType();
|
||||
GadgetContent content = switch (type) {
|
||||
case GatherPoint -> new GadgetGatherPoint(this);
|
||||
case GatherObject -> new GadgetGatherObject(this);
|
||||
case Worktop -> new GadgetWorktop(this);
|
||||
case RewardStatue -> new GadgetRewardStatue(this);
|
||||
case Chest -> new GadgetChest(this);
|
||||
case Gadget -> new GadgetObject(this);
|
||||
default -> null;
|
||||
};
|
||||
default -> null;
|
||||
};
|
||||
|
||||
this.content = content;
|
||||
}
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
|
||||
return this.fightProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
|
||||
return this.fightProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInteract(Player player, GadgetInteractReq interactReq) {
|
||||
if (this.getContent() == null) {
|
||||
if (this.getContent() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -165,68 +165,68 @@ public class EntityGadget extends EntityBaseGadget {
|
||||
if (shouldDelete) {
|
||||
this.getScene().killEntity(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
if (this.getSpawnEntry() != null) {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
if (getScene().getChallenge() != null) {
|
||||
getScene().getChallenge().onGadgetDeath(this);
|
||||
}
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
if (this.getSpawnEntry() != null) {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
if (getScene().getChallenge() != null) {
|
||||
getScene().getChallenge().onGadgetDeath(this);
|
||||
}
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
// We do not use the getter to null check because the getter will create a fight prop map if it is null
|
||||
if (this.fightProp != null) {
|
||||
this.addAllFightPropsToEntityInfo(entityInfo);
|
||||
}
|
||||
// We do not use the getter to null check because the getter will create a fight prop map if it is null
|
||||
if (this.fightProp != null) {
|
||||
this.addAllFightPropsToEntityInfo(entityInfo);
|
||||
}
|
||||
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getGadgetId())
|
||||
.setGroupId(this.getGroupId())
|
||||
.setConfigId(this.getConfigId())
|
||||
.setGadgetState(this.getState())
|
||||
.setIsEnableInteract(true)
|
||||
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getGadgetId())
|
||||
.setGroupId(this.getGroupId())
|
||||
.setConfigId(this.getConfigId())
|
||||
.setGadgetState(this.getState())
|
||||
.setIsEnableInteract(true)
|
||||
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
|
||||
|
||||
if (this.getContent() != null) {
|
||||
this.getContent().onBuildProto(gadgetInfo);
|
||||
}
|
||||
if (this.getContent() != null) {
|
||||
this.getContent().onBuildProto(gadgetInfo);
|
||||
}
|
||||
|
||||
entityInfo.setGadget(gadgetInfo);
|
||||
entityInfo.setGadget(gadgetInfo);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
return entityInfo.build();
|
||||
}
|
||||
}
|
||||
|
@ -28,95 +28,95 @@ import emu.grasscutter.utils.ProtoHelper;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
|
||||
public class EntityItem extends EntityBaseGadget {
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
|
||||
private final GameItem item;
|
||||
private final long guid;
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
|
||||
private final boolean share;
|
||||
private final GameItem item;
|
||||
private final long guid;
|
||||
|
||||
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
|
||||
super(scene);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
|
||||
this.item = new GameItem(itemData, count);
|
||||
this.share = true;
|
||||
}
|
||||
private final boolean share;
|
||||
|
||||
// In official game, some drop items are shared to all players, and some other items are independent to all players
|
||||
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
|
||||
// but if you broke regional mine, when someone picked up the drop then it disappeared
|
||||
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
|
||||
super(scene);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
|
||||
this.item = new GameItem(itemData, count);
|
||||
this.share = share;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
private GameItem getItem() {
|
||||
return this.item;
|
||||
}
|
||||
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
|
||||
super(scene);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
|
||||
this.item = new GameItem(itemData, count);
|
||||
this.share = true;
|
||||
}
|
||||
|
||||
public ItemData getItemData() {
|
||||
return this.getItem().getItemData();
|
||||
}
|
||||
// In official game, some drop items are shared to all players, and some other items are independent to all players
|
||||
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
|
||||
// but if you broke regional mine, when someone picked up the drop then it disappeared
|
||||
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
|
||||
super(scene);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
|
||||
this.item = new GameItem(itemData, count);
|
||||
this.share = share;
|
||||
}
|
||||
|
||||
public long getGuid() {
|
||||
return guid;
|
||||
}
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return this.getItem().getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGadgetId() {
|
||||
return this.getItemData().getGadgetId();
|
||||
}
|
||||
private GameItem getItem() {
|
||||
return this.item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
public ItemData getItemData() {
|
||||
return this.getItem().getItemData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return null;
|
||||
}
|
||||
public long getGuid() {
|
||||
return guid;
|
||||
}
|
||||
|
||||
public boolean isShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInteract(Player player, GadgetInteractReq interactReq) {
|
||||
// check drop owner to avoid someone picked up item in others' world
|
||||
public int getCount() {
|
||||
return this.getItem().getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGadgetId() {
|
||||
return this.getItemData().getGadgetId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isShare() {
|
||||
return share;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInteract(Player player, GadgetInteractReq interactReq) {
|
||||
// check drop owner to avoid someone picked up item in others' world
|
||||
if (!this.isShare()) {
|
||||
int dropOwner = (int) (this.getGuid() >> 32);
|
||||
if (dropOwner != player.getUid()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.getScene().removeEntity(this);
|
||||
GameItem item = new GameItem(this.getItemData(), this.getCount());
|
||||
|
||||
|
||||
// Add to inventory
|
||||
boolean success = player.getInventory().addItem(item, ActionReason.SubfieldDrop);
|
||||
if (success) {
|
||||
@ -128,39 +128,39 @@ public class EntityItem extends EntityBaseGadget {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getItemData().getGadgetId())
|
||||
.setTrifleItem(this.getItem().toProto())
|
||||
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
|
||||
.setAuthorityPeerId(this.getWorld().getHostPeerId())
|
||||
.setIsEnableInteract(true);
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(Vector.newBuilder()))
|
||||
.setBornPos(Vector.newBuilder())
|
||||
.build();
|
||||
|
||||
entityInfo.setGadget(gadgetInfo);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 1))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getItemData().getGadgetId())
|
||||
.setTrifleItem(this.getItem().toProto())
|
||||
.setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR)
|
||||
.setAuthorityPeerId(this.getWorld().getHostPeerId())
|
||||
.setIsEnableInteract(true);
|
||||
|
||||
entityInfo.setGadget(gadgetInfo);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
}
|
||||
|
@ -39,257 +39,257 @@ import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
|
||||
public class EntityMonster extends GameEntity {
|
||||
private final MonsterData monsterData;
|
||||
private final Int2FloatOpenHashMap fightProp;
|
||||
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
private final Position bornPos;
|
||||
private final int level;
|
||||
private int weaponEntityId;
|
||||
private int poseId;
|
||||
|
||||
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
|
||||
super(scene);
|
||||
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
|
||||
this.monsterData = monsterData;
|
||||
this.fightProp = new Int2FloatOpenHashMap();
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.bornPos = getPosition().clone();
|
||||
this.level = level;
|
||||
|
||||
// Monster weapon
|
||||
if (getMonsterWeaponId() > 0) {
|
||||
this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
|
||||
}
|
||||
|
||||
this.recalcStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
private final MonsterData monsterData;
|
||||
private final Int2FloatOpenHashMap fightProp;
|
||||
|
||||
public MonsterData getMonsterData() {
|
||||
return monsterData;
|
||||
}
|
||||
|
||||
public int getMonsterWeaponId() {
|
||||
return getMonsterData().getWeaponId();
|
||||
}
|
||||
|
||||
private int getMonsterId() {
|
||||
return this.getMonsterData().getId();
|
||||
}
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
private final Position bornPos;
|
||||
private final int level;
|
||||
private int weaponEntityId;
|
||||
private int poseId;
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
|
||||
super(scene);
|
||||
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
|
||||
this.monsterData = monsterData;
|
||||
this.fightProp = new Int2FloatOpenHashMap();
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position();
|
||||
this.bornPos = getPosition().clone();
|
||||
this.level = level;
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
// Monster weapon
|
||||
if (getMonsterWeaponId() > 0) {
|
||||
this.weaponEntityId = getWorld().getNextEntityId(EntityIdType.WEAPON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
public Position getBornPos() {
|
||||
return bornPos;
|
||||
}
|
||||
this.recalcStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return fightProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
||||
}
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getPoseId() {
|
||||
return poseId;
|
||||
}
|
||||
public MonsterData getMonsterData() {
|
||||
return monsterData;
|
||||
}
|
||||
|
||||
public void setPoseId(int poseId) {
|
||||
this.poseId = poseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMonsterWeaponId() {
|
||||
return getMonsterData().getWeaponId();
|
||||
}
|
||||
|
||||
private int getMonsterId() {
|
||||
return this.getMonsterData().getId();
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
public Position getBornPos() {
|
||||
return bornPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return fightProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
|
||||
}
|
||||
|
||||
public int getPoseId() {
|
||||
return poseId;
|
||||
}
|
||||
|
||||
public void setPoseId(int poseId) {
|
||||
this.poseId = poseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInteract(Player player, GadgetInteractReq interactReq) {
|
||||
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
|
||||
|
||||
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
|
||||
|
||||
if (gatherData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.getInventory().addItem(gatherData.getGatherItem(), ActionReason.SubfieldDrop);
|
||||
|
||||
|
||||
this.getScene().killEntity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float amount, int killerId) {
|
||||
// Get HP before damage.
|
||||
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
// Apply damage.
|
||||
super.damage(amount, killerId);
|
||||
@Override
|
||||
public void damage(float amount, int killerId) {
|
||||
// Get HP before damage.
|
||||
float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
|
||||
// Get HP after damage.
|
||||
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
// Apply damage.
|
||||
super.damage(amount, killerId);
|
||||
|
||||
// Invoke energy drop logic.
|
||||
for (Player player : this.getScene().getPlayers()) {
|
||||
player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage);
|
||||
}
|
||||
}
|
||||
// Get HP after damage.
|
||||
float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
if (this.getSpawnEntry() != null) {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
// first set the challenge data
|
||||
if (getScene().getChallenge() != null) {
|
||||
getScene().getChallenge().onMonsterDeath(this);
|
||||
}
|
||||
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
|
||||
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
|
||||
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
|
||||
}
|
||||
// prevent spawn monster after success
|
||||
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}else if(getScene().getChallenge() == null){
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}
|
||||
}
|
||||
// Battle Pass trigger
|
||||
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
|
||||
}
|
||||
|
||||
public void recalcStats() {
|
||||
// Monster data
|
||||
MonsterData data = this.getMonsterData();
|
||||
// Invoke energy drop logic.
|
||||
for (Player player : this.getScene().getPlayers()) {
|
||||
player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage);
|
||||
}
|
||||
}
|
||||
|
||||
// Get hp percent, set to 100% if none
|
||||
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
// Clear properties
|
||||
this.getFightProperties().clear();
|
||||
|
||||
// Base stats
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
|
||||
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
|
||||
|
||||
// Level curve
|
||||
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
|
||||
if (curve != null) {
|
||||
for (PropGrowCurve growCurve : data.getPropGrowCurves()) {
|
||||
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
|
||||
this.setFightProperty(prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
|
||||
}
|
||||
}
|
||||
|
||||
// Set % stats
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_MAX_HP,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
|
||||
);
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_ATTACK,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
|
||||
);
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_DEFENSE,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
|
||||
);
|
||||
|
||||
// Set current hp
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
|
||||
}
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
if (this.getSpawnEntry() != null) {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
// first set the challenge data
|
||||
if (getScene().getChallenge() != null) {
|
||||
getScene().getChallenge().onMonsterDeath(this);
|
||||
}
|
||||
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
|
||||
if (getScene().getScriptManager().getScriptMonsterSpawnService() != null) {
|
||||
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
|
||||
}
|
||||
// prevent spawn monster after success
|
||||
if (getScene().getChallenge() != null && getScene().getChallenge().inProgress()) {
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}else if (getScene().getChallenge() == null) {
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}
|
||||
}
|
||||
// Battle Pass trigger
|
||||
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
|
||||
.setBornPos(this.getBornPos().toProto())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
|
||||
.setMotionInfo(this.getMotionInfo())
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(this.getLifeState().getValue());
|
||||
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
|
||||
.setMonsterId(getMonsterId())
|
||||
.setGroupId(this.getGroupId())
|
||||
.setConfigId(this.getConfigId())
|
||||
.addAllAffixList(getMonsterData().getAffix())
|
||||
.setAuthorityPeerId(getWorld().getHostPeerId())
|
||||
.setPoseId(this.getPoseId())
|
||||
.setBlockId(3001)
|
||||
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT)
|
||||
.setSpecialNameId(40);
|
||||
|
||||
if (getMonsterData().getDescribeData() != null) {
|
||||
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleID());
|
||||
}
|
||||
|
||||
if (this.getMonsterWeaponId() > 0) {
|
||||
SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
|
||||
.setEntityId(this.weaponEntityId)
|
||||
.setGadgetId(this.getMonsterWeaponId())
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.build();
|
||||
|
||||
monsterInfo.addWeaponList(weaponInfo);
|
||||
}
|
||||
|
||||
entityInfo.setMonster(monsterInfo);
|
||||
public void recalcStats() {
|
||||
// Monster data
|
||||
MonsterData data = this.getMonsterData();
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
// Get hp percent, set to 100% if none
|
||||
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
// Clear properties
|
||||
this.getFightProperties().clear();
|
||||
|
||||
// Base stats
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
|
||||
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
|
||||
|
||||
// Level curve
|
||||
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
|
||||
if (curve != null) {
|
||||
for (PropGrowCurve growCurve : data.getPropGrowCurves()) {
|
||||
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
|
||||
this.setFightProperty(prop, this.getFightProperty(prop) * curve.getMultByProp(growCurve.getGrowCurve()));
|
||||
}
|
||||
}
|
||||
|
||||
// Set % stats
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_MAX_HP,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
|
||||
);
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_ATTACK,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
|
||||
);
|
||||
this.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_DEFENSE,
|
||||
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
|
||||
);
|
||||
|
||||
// Set current hp
|
||||
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
|
||||
.setBornPos(this.getBornPos().toProto())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
|
||||
.setMotionInfo(this.getMotionInfo())
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(this.getLifeState().getValue());
|
||||
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
|
||||
.build();
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder()
|
||||
.setMonsterId(getMonsterId())
|
||||
.setGroupId(this.getGroupId())
|
||||
.setConfigId(this.getConfigId())
|
||||
.addAllAffixList(getMonsterData().getAffix())
|
||||
.setAuthorityPeerId(getWorld().getHostPeerId())
|
||||
.setPoseId(this.getPoseId())
|
||||
.setBlockId(3001)
|
||||
.setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT)
|
||||
.setSpecialNameId(40);
|
||||
|
||||
if (getMonsterData().getDescribeData() != null) {
|
||||
monsterInfo.setTitleId(getMonsterData().getDescribeData().getTitleID());
|
||||
}
|
||||
|
||||
if (this.getMonsterWeaponId() > 0) {
|
||||
SceneWeaponInfo weaponInfo = SceneWeaponInfo.newBuilder()
|
||||
.setEntityId(this.weaponEntityId)
|
||||
.setGadgetId(this.getMonsterWeaponId())
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.build();
|
||||
|
||||
monsterInfo.addWeaponList(weaponInfo);
|
||||
}
|
||||
|
||||
entityInfo.setMonster(monsterInfo);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
}
|
||||
|
@ -31,104 +31,104 @@ import java.util.ArrayList;
|
||||
|
||||
public class EntityVehicle extends EntityBaseGadget {
|
||||
|
||||
private final Player owner;
|
||||
private final Int2FloatOpenHashMap fightProp;
|
||||
private final Player owner;
|
||||
private final Int2FloatOpenHashMap fightProp;
|
||||
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
private final Position pos;
|
||||
private final Position rot;
|
||||
|
||||
private final int pointId;
|
||||
private final int gadgetId;
|
||||
private final int pointId;
|
||||
private final int gadgetId;
|
||||
|
||||
private float curStamina;
|
||||
private List<VehicleMember> vehicleMembers;
|
||||
private float curStamina;
|
||||
private List<VehicleMember> vehicleMembers;
|
||||
|
||||
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
|
||||
super(scene);
|
||||
this.owner = player;
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.fightProp = new Int2FloatOpenHashMap();
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position(rot);
|
||||
this.gadgetId = gadgetId;
|
||||
this.pointId = pointId;
|
||||
this.curStamina = 240;
|
||||
this.vehicleMembers = new ArrayList<VehicleMember>();
|
||||
}
|
||||
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
|
||||
super(scene);
|
||||
this.owner = player;
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
|
||||
this.fightProp = new Int2FloatOpenHashMap();
|
||||
this.pos = new Position(pos);
|
||||
this.rot = new Position(rot);
|
||||
this.gadgetId = gadgetId;
|
||||
this.pointId = pointId;
|
||||
this.curStamina = 240;
|
||||
this.vehicleMembers = new ArrayList<VehicleMember>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGadgetId() { return gadgetId; }
|
||||
@Override
|
||||
public int getGadgetId() { return gadgetId; }
|
||||
|
||||
public Player getOwner() {
|
||||
return owner;
|
||||
}
|
||||
public Player getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public float getCurStamina() { return curStamina; }
|
||||
public float getCurStamina() { return curStamina; }
|
||||
|
||||
public void setCurStamina(float stamina) { this.curStamina = stamina; }
|
||||
public void setCurStamina(float stamina) { this.curStamina = stamina; }
|
||||
|
||||
public int getPointId() { return pointId; }
|
||||
public int getPointId() { return pointId; }
|
||||
|
||||
public List<VehicleMember> getVehicleMembers() { return vehicleMembers; }
|
||||
public List<VehicleMember> getVehicleMembers() { return vehicleMembers; }
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return fightProp;
|
||||
}
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return fightProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() { return this.pos; }
|
||||
@Override
|
||||
public Position getPosition() { return this.pos; }
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
VehicleInfo vehicle = VehicleInfo.newBuilder()
|
||||
.setOwnerUid(this.owner.getUid())
|
||||
.setCurStamina(getCurStamina())
|
||||
.build();
|
||||
@Override
|
||||
public SceneEntityInfo toProto() {
|
||||
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
|
||||
.setBornPos(getPosition().toProto())
|
||||
.build();
|
||||
VehicleInfo vehicle = VehicleInfo.newBuilder()
|
||||
.setOwnerUid(this.owner.getUid())
|
||||
.setCurStamina(getCurStamina())
|
||||
.build();
|
||||
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getGadgetId())
|
||||
.setAuthorityPeerId(this.getOwner().getPeerId())
|
||||
.setIsEnableInteract(true)
|
||||
.setVehicleInfo(vehicle);
|
||||
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(getPosition().toProto()))
|
||||
.setBornPos(getPosition().toProto())
|
||||
.build();
|
||||
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setGadget(gadgetInfo)
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder()
|
||||
.setGadgetId(this.getGadgetId())
|
||||
.setAuthorityPeerId(this.getOwner().getPeerId())
|
||||
.setIsEnableInteract(true)
|
||||
.setVehicleInfo(vehicle);
|
||||
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
|
||||
.build();
|
||||
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET)
|
||||
.setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setGadget(gadgetInfo)
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
PropPair pair = PropPair.newBuilder()
|
||||
.setType(PlayerProperty.PROP_LEVEL.getId())
|
||||
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, 47))
|
||||
.build();
|
||||
|
||||
entityInfo.addPropList(pair);
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
entityInfo.addPropList(pair);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
}
|
||||
|
@ -23,236 +23,236 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public abstract class GameEntity {
|
||||
protected int id;
|
||||
private final Scene scene;
|
||||
private SpawnDataEntry spawnEntry;
|
||||
|
||||
private int blockId;
|
||||
private int configId;
|
||||
private int groupId;
|
||||
|
||||
private MotionState moveState;
|
||||
private int lastMoveSceneTimeMs;
|
||||
private int lastMoveReliableSeq;
|
||||
|
||||
// Abilities
|
||||
private Map<String, Float> metaOverrideMap;
|
||||
private Int2ObjectMap<String> metaModifiers;
|
||||
|
||||
public GameEntity(Scene scene) {
|
||||
this.scene = scene;
|
||||
this.moveState = MotionState.MOTION_STATE_NONE;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public int getEntityType() {
|
||||
return getId() >> 24;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return this.getScene().getWorld();
|
||||
}
|
||||
protected int id;
|
||||
private final Scene scene;
|
||||
private SpawnDataEntry spawnEntry;
|
||||
|
||||
public Scene getScene() {
|
||||
return this.scene;
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return true;
|
||||
}
|
||||
private int blockId;
|
||||
private int configId;
|
||||
private int groupId;
|
||||
|
||||
public LifeState getLifeState() {
|
||||
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
||||
}
|
||||
|
||||
public Map<String, Float> getMetaOverrideMap() {
|
||||
if (this.metaOverrideMap == null) {
|
||||
this.metaOverrideMap = new HashMap<>();
|
||||
}
|
||||
return this.metaOverrideMap;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<String> getMetaModifiers() {
|
||||
if (this.metaModifiers == null) {
|
||||
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
return this.metaModifiers;
|
||||
}
|
||||
private MotionState moveState;
|
||||
private int lastMoveSceneTimeMs;
|
||||
private int lastMoveReliableSeq;
|
||||
|
||||
public abstract Int2FloatOpenHashMap getFightProperties();
|
||||
|
||||
public abstract Position getPosition();
|
||||
|
||||
public abstract Position getRotation();
|
||||
|
||||
public MotionState getMotionState() {
|
||||
return moveState;
|
||||
}
|
||||
// Abilities
|
||||
private Map<String, Float> metaOverrideMap;
|
||||
private Int2ObjectMap<String> metaModifiers;
|
||||
|
||||
public void setMotionState(MotionState moveState) {
|
||||
this.moveState = moveState;
|
||||
}
|
||||
public GameEntity(Scene scene) {
|
||||
this.scene = scene;
|
||||
this.moveState = MotionState.MOTION_STATE_NONE;
|
||||
}
|
||||
|
||||
public int getLastMoveSceneTimeMs() {
|
||||
return lastMoveSceneTimeMs;
|
||||
}
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void setLastMoveSceneTimeMs(int lastMoveSceneTimeMs) {
|
||||
this.lastMoveSceneTimeMs = lastMoveSceneTimeMs;
|
||||
}
|
||||
public int getEntityType() {
|
||||
return getId() >> 24;
|
||||
}
|
||||
|
||||
public int getLastMoveReliableSeq() {
|
||||
return lastMoveReliableSeq;
|
||||
}
|
||||
public World getWorld() {
|
||||
return this.getScene().getWorld();
|
||||
}
|
||||
|
||||
public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
|
||||
this.lastMoveReliableSeq = lastMoveReliableSeq;
|
||||
}
|
||||
|
||||
public void setFightProperty(FightProperty prop, float value) {
|
||||
this.getFightProperties().put(prop.getId(), value);
|
||||
}
|
||||
|
||||
private void setFightProperty(int id, float value) {
|
||||
this.getFightProperties().put(id, value);
|
||||
}
|
||||
|
||||
public void addFightProperty(FightProperty prop, float value) {
|
||||
this.getFightProperties().put(prop.getId(), getFightProperty(prop) + value);
|
||||
}
|
||||
|
||||
public float getFightProperty(FightProperty prop) {
|
||||
return getFightProperties().getOrDefault(prop.getId(), 0f);
|
||||
}
|
||||
|
||||
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBlockId() {
|
||||
return blockId;
|
||||
}
|
||||
public Scene getScene() {
|
||||
return this.scene;
|
||||
}
|
||||
|
||||
public void setBlockId(int blockId) {
|
||||
this.blockId = blockId;
|
||||
}
|
||||
|
||||
public int getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
public boolean isAlive() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setConfigId(int configId) {
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
public LifeState getLifeState() {
|
||||
return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD;
|
||||
}
|
||||
|
||||
public void setGroupId(int groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
protected MotionInfo getMotionInfo() {
|
||||
MotionInfo proto = MotionInfo.newBuilder()
|
||||
.setPos(getPosition().toProto())
|
||||
.setRot(getRotation().toProto())
|
||||
.setSpeed(Vector.newBuilder())
|
||||
.setState(this.getMotionState())
|
||||
.build();
|
||||
|
||||
return proto;
|
||||
}
|
||||
public Map<String, Float> getMetaOverrideMap() {
|
||||
if (this.metaOverrideMap == null) {
|
||||
this.metaOverrideMap = new HashMap<>();
|
||||
}
|
||||
return this.metaOverrideMap;
|
||||
}
|
||||
|
||||
public SpawnDataEntry getSpawnEntry() {
|
||||
return spawnEntry;
|
||||
}
|
||||
public Int2ObjectMap<String> getMetaModifiers() {
|
||||
if (this.metaModifiers == null) {
|
||||
this.metaModifiers = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
return this.metaModifiers;
|
||||
}
|
||||
|
||||
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
|
||||
this.spawnEntry = spawnEntry;
|
||||
}
|
||||
|
||||
public float heal(float amount) {
|
||||
if (this.getFightProperties() == null) {
|
||||
return 0f;
|
||||
}
|
||||
public abstract Int2FloatOpenHashMap getFightProperties();
|
||||
|
||||
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
if (curHp >= maxHp) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float healed = Math.min(maxHp - curHp, amount);
|
||||
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
|
||||
|
||||
getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void damage(float amount) {
|
||||
damage(amount, 0);
|
||||
}
|
||||
|
||||
public void damage(float amount, int killerId) {
|
||||
// Sanity check
|
||||
if (getFightProperties() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lose hp
|
||||
addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount);
|
||||
|
||||
// Check if dead
|
||||
boolean isDead = false;
|
||||
if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
// Packets
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
// Check if dead
|
||||
if (isDead) {
|
||||
getScene().killEntity(this, killerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
public abstract Position getPosition();
|
||||
|
||||
public abstract Position getRotation();
|
||||
|
||||
public MotionState getMotionState() {
|
||||
return moveState;
|
||||
}
|
||||
|
||||
public void setMotionState(MotionState moveState) {
|
||||
this.moveState = moveState;
|
||||
}
|
||||
|
||||
public int getLastMoveSceneTimeMs() {
|
||||
return lastMoveSceneTimeMs;
|
||||
}
|
||||
|
||||
public void setLastMoveSceneTimeMs(int lastMoveSceneTimeMs) {
|
||||
this.lastMoveSceneTimeMs = lastMoveSceneTimeMs;
|
||||
}
|
||||
|
||||
public int getLastMoveReliableSeq() {
|
||||
return lastMoveReliableSeq;
|
||||
}
|
||||
|
||||
public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
|
||||
this.lastMoveReliableSeq = lastMoveReliableSeq;
|
||||
}
|
||||
|
||||
public void setFightProperty(FightProperty prop, float value) {
|
||||
this.getFightProperties().put(prop.getId(), value);
|
||||
}
|
||||
|
||||
private void setFightProperty(int id, float value) {
|
||||
this.getFightProperties().put(id, value);
|
||||
}
|
||||
|
||||
public void addFightProperty(FightProperty prop, float value) {
|
||||
this.getFightProperties().put(prop.getId(), getFightProperty(prop) + value);
|
||||
}
|
||||
|
||||
public float getFightProperty(FightProperty prop) {
|
||||
return getFightProperties().getOrDefault(prop.getId(), 0f);
|
||||
}
|
||||
|
||||
public void addAllFightPropsToEntityInfo(SceneEntityInfo.Builder entityInfo) {
|
||||
for (Int2FloatMap.Entry entry : getFightProperties().int2FloatEntrySet()) {
|
||||
if (entry.getIntKey() == 0) {
|
||||
continue;
|
||||
}
|
||||
FightPropPair fightProp = FightPropPair.newBuilder().setPropType(entry.getIntKey()).setPropValue(entry.getFloatValue()).build();
|
||||
entityInfo.addFightPropList(fightProp);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBlockId() {
|
||||
return blockId;
|
||||
}
|
||||
|
||||
public void setBlockId(int blockId) {
|
||||
this.blockId = blockId;
|
||||
}
|
||||
|
||||
public int getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
|
||||
public void setConfigId(int configId) {
|
||||
this.configId = configId;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(int groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
protected MotionInfo getMotionInfo() {
|
||||
MotionInfo proto = MotionInfo.newBuilder()
|
||||
.setPos(getPosition().toProto())
|
||||
.setRot(getRotation().toProto())
|
||||
.setSpeed(Vector.newBuilder())
|
||||
.setState(this.getMotionState())
|
||||
.build();
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
public SpawnDataEntry getSpawnEntry() {
|
||||
return spawnEntry;
|
||||
}
|
||||
|
||||
public void setSpawnEntry(SpawnDataEntry spawnEntry) {
|
||||
this.spawnEntry = spawnEntry;
|
||||
}
|
||||
|
||||
public float heal(float amount) {
|
||||
if (this.getFightProperties() == null) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
if (curHp >= maxHp) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float healed = Math.min(maxHp - curHp, amount);
|
||||
this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed);
|
||||
|
||||
getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
return healed;
|
||||
}
|
||||
|
||||
public void damage(float amount) {
|
||||
damage(amount, 0);
|
||||
}
|
||||
|
||||
public void damage(float amount, int killerId) {
|
||||
// Sanity check
|
||||
if (getFightProperties() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lose hp
|
||||
addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount);
|
||||
|
||||
// Check if dead
|
||||
boolean isDead = false;
|
||||
if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) {
|
||||
setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
|
||||
isDead = true;
|
||||
}
|
||||
|
||||
// Packets
|
||||
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
// Check if dead
|
||||
if (isDead) {
|
||||
getScene().killEntity(this, killerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player interacts with this entity
|
||||
* @param player Player that is interacting with this entity
|
||||
* @param interactReq Interact request protobuf data
|
||||
* @param interactReq Interact request protobuf data
|
||||
*/
|
||||
public void onInteract(Player player, GadgetInteractReq interactReq) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when this entity is added to the world
|
||||
*/
|
||||
public void onCreate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
public void onCreate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this entity dies
|
||||
* @param killerId Entity id of the entity that killed this entity
|
||||
*/
|
||||
public void onDeath(int killerId) {
|
||||
|
||||
}
|
||||
|
||||
public abstract SceneEntityInfo toProto();
|
||||
public void onDeath(int killerId) {
|
||||
|
||||
}
|
||||
|
||||
public abstract SceneEntityInfo toProto();
|
||||
}
|
||||
|
@ -15,51 +15,51 @@ import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
|
||||
public class GadgetChest extends GadgetContent {
|
||||
|
||||
public GadgetChest(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap();
|
||||
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||
if(handler == null){
|
||||
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
|
||||
return false;
|
||||
}
|
||||
public GadgetChest(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
if(req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()){
|
||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
||||
return false;
|
||||
}else{
|
||||
var success = handler.onInteract(this, player);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataSystem().getChestInteractHandlerMap();
|
||||
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||
if (handler == null) {
|
||||
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
|
||||
return false;
|
||||
}
|
||||
|
||||
getGadget().updateState(ScriptGadgetState.ChestOpened);
|
||||
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (req.getOpType() == InterOpType.INTER_OP_TYPE_START && handler.isTwoStep()) {
|
||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_TYPE_OPEN_CHEST, InterOpType.INTER_OP_TYPE_START));
|
||||
return false;
|
||||
}else {
|
||||
var success = handler.onInteract(this, player);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
if(getGadget().getMetaGadget() == null){
|
||||
return;
|
||||
}
|
||||
getGadget().updateState(ScriptGadgetState.ChestOpened);
|
||||
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_OPEN_CHEST));
|
||||
|
||||
var bossChest = getGadget().getMetaGadget().boss_chest;
|
||||
if(bossChest != null){
|
||||
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
|
||||
.setMonsterConfigId(bossChest.monster_config_id)
|
||||
.setResin(bossChest.resin)
|
||||
.addAllQualifyUidList(players)
|
||||
.addAllRemainUidList(players)
|
||||
.build());
|
||||
}
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
if (getGadget().getMetaGadget() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
var bossChest = getGadget().getMetaGadget().boss_chest;
|
||||
if (bossChest != null) {
|
||||
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
|
||||
|
||||
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
|
||||
.setMonsterConfigId(bossChest.monster_config_id)
|
||||
.setResin(bossChest.resin)
|
||||
.addAllQualifyUidList(players)
|
||||
.addAllRemainUidList(players)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public class BossChestInteractHandler implements ChestInteractHandler{
|
||||
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
|
||||
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
||||
|
||||
if(reward == null){
|
||||
if (reward == null) {
|
||||
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
|
||||
return false;
|
||||
}
|
||||
|
@ -24,16 +24,16 @@ public class ExpeditionSystem extends BaseGameSystem {
|
||||
this.expeditionRewardData = new Int2ObjectOpenHashMap<>();
|
||||
this.load();
|
||||
}
|
||||
|
||||
public Int2ObjectMap<List<ExpeditionRewardDataList>> getExpeditionRewardDataList() {
|
||||
return expeditionRewardData;
|
||||
|
||||
public Int2ObjectMap<List<ExpeditionRewardDataList>> getExpeditionRewardDataList() {
|
||||
return expeditionRewardData;
|
||||
}
|
||||
|
||||
public synchronized void load() {
|
||||
try (Reader fileReader = DataLoader.loadReader("ExpeditionReward.json")) {
|
||||
getExpeditionRewardDataList().clear();
|
||||
List<ExpeditionRewardInfo> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType());
|
||||
if(banners.size() > 0) {
|
||||
if (banners.size() > 0) {
|
||||
for (ExpeditionRewardInfo di : banners) {
|
||||
getExpeditionRewardDataList().put(di.getExpId(), di.getExpeditionRewardDataList());
|
||||
}
|
||||
|
@ -15,243 +15,243 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class FriendsList extends BasePlayerManager {
|
||||
private final Int2ObjectMap<Friendship> friends;
|
||||
private final Int2ObjectMap<Friendship> pendingFriends;
|
||||
|
||||
private boolean loaded = false;
|
||||
|
||||
public FriendsList(Player player) {
|
||||
super(player);
|
||||
this.friends = new Int2ObjectOpenHashMap<Friendship>();
|
||||
this.pendingFriends = new Int2ObjectOpenHashMap<Friendship>();
|
||||
}
|
||||
|
||||
public boolean hasLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public synchronized Int2ObjectMap<Friendship> getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
public synchronized Int2ObjectMap<Friendship> getPendingFriends() {
|
||||
return this.pendingFriends;
|
||||
}
|
||||
|
||||
public synchronized boolean isFriendsWith(int uid) {
|
||||
return this.getFriends().containsKey(uid);
|
||||
}
|
||||
|
||||
private synchronized Friendship getFriendshipById(int id) {
|
||||
Friendship friendship = this.getFriends().get(id);
|
||||
if (friendship == null) {
|
||||
friendship = this.getPendingFriendById(id);
|
||||
}
|
||||
return friendship;
|
||||
}
|
||||
|
||||
private synchronized Friendship getFriendById(int id) {
|
||||
return this.getFriends().get(id);
|
||||
}
|
||||
|
||||
private synchronized Friendship getPendingFriendById(int id) {
|
||||
return this.getPendingFriends().get(id);
|
||||
}
|
||||
|
||||
public void addFriend(Friendship friendship) {
|
||||
getFriends().put(friendship.getFriendId(), friendship);
|
||||
}
|
||||
|
||||
public void addPendingFriend(Friendship friendship) {
|
||||
getPendingFriends().put(friendship.getFriendId(), friendship);
|
||||
}
|
||||
|
||||
public synchronized void handleFriendRequest(int targetUid, DealAddFriendResultType result) {
|
||||
// Check if player has sent friend request
|
||||
Friendship myFriendship = this.getPendingFriendById(targetUid);
|
||||
if (myFriendship == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure asker cant do anything
|
||||
if (myFriendship.getAskerId() == this.getPlayer().getUid()) {
|
||||
return;
|
||||
}
|
||||
private final Int2ObjectMap<Friendship> friends;
|
||||
private final Int2ObjectMap<Friendship> pendingFriends;
|
||||
|
||||
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
|
||||
if (target == null) {
|
||||
return; // Should never happen
|
||||
}
|
||||
private boolean loaded = false;
|
||||
|
||||
// Get target's friendship
|
||||
Friendship theirFriendship = null;
|
||||
if (target.isOnline()) {
|
||||
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
|
||||
} else {
|
||||
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
|
||||
}
|
||||
public FriendsList(Player player) {
|
||||
super(player);
|
||||
this.friends = new Int2ObjectOpenHashMap<Friendship>();
|
||||
this.pendingFriends = new Int2ObjectOpenHashMap<Friendship>();
|
||||
}
|
||||
|
||||
if (theirFriendship == null) {
|
||||
// They dont have us on their friends list anymore, rip
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
myFriendship.delete();
|
||||
return;
|
||||
}
|
||||
public boolean hasLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// Handle
|
||||
if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT) { // Request accepted
|
||||
myFriendship.setIsFriend(true);
|
||||
theirFriendship.setIsFriend(true);
|
||||
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
this.addFriend(myFriendship);
|
||||
|
||||
if (target.isOnline()) {
|
||||
target.getFriendsList().getPendingFriends().remove(this.getPlayer().getUid());
|
||||
target.getFriendsList().addFriend(theirFriendship);
|
||||
}
|
||||
|
||||
myFriendship.save();
|
||||
theirFriendship.save();
|
||||
} else { // Request declined
|
||||
// Delete from my pending friends
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
myFriendship.delete();
|
||||
// Delete from target uid
|
||||
if (target.isOnline()) {
|
||||
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
|
||||
}
|
||||
theirFriendship.delete();
|
||||
}
|
||||
|
||||
// Packet
|
||||
this.getPlayer().sendPacket(new PacketDealAddFriendRsp(targetUid, result));
|
||||
}
|
||||
|
||||
public synchronized void deleteFriend(int targetUid) {
|
||||
Friendship myFriendship = this.getFriendById(targetUid);
|
||||
if (myFriendship == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getFriends().remove(targetUid);
|
||||
myFriendship.delete();
|
||||
|
||||
Friendship theirFriendship = null;
|
||||
Player friend = myFriendship.getFriendProfile().getPlayer();
|
||||
if (friend != null) {
|
||||
// Friend online
|
||||
theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getUid());
|
||||
if (theirFriendship != null) {
|
||||
friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId());
|
||||
theirFriendship.delete();
|
||||
friend.sendPacket(new PacketDeleteFriendNotify(theirFriendship.getFriendId()));
|
||||
}
|
||||
} else {
|
||||
// Friend offline
|
||||
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
|
||||
if (theirFriendship != null) {
|
||||
theirFriendship.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Packet
|
||||
this.getPlayer().sendPacket(new PacketDeleteFriendRsp(targetUid));
|
||||
}
|
||||
|
||||
public synchronized void sendFriendRequest(int targetUid) {
|
||||
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
|
||||
public synchronized Int2ObjectMap<Friendship> getFriends() {
|
||||
return friends;
|
||||
}
|
||||
|
||||
if (target == null || target == this.getPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if friend already exists
|
||||
if (this.getPendingFriends().containsKey(targetUid) || this.getFriends().containsKey(targetUid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create friendships
|
||||
Friendship myFriendship = new Friendship(getPlayer(), target, getPlayer());
|
||||
Friendship theirFriendship = new Friendship(target, getPlayer(), getPlayer());
|
||||
|
||||
// Add pending lists
|
||||
this.addPendingFriend(myFriendship);
|
||||
public synchronized Int2ObjectMap<Friendship> getPendingFriends() {
|
||||
return this.pendingFriends;
|
||||
}
|
||||
|
||||
if (target.isOnline() && target.getFriendsList().hasLoaded()) {
|
||||
target.getFriendsList().addPendingFriend(theirFriendship);
|
||||
target.sendPacket(new PacketAskAddFriendNotify(theirFriendship));
|
||||
}
|
||||
|
||||
// Save
|
||||
myFriendship.save();
|
||||
theirFriendship.save();
|
||||
|
||||
// Packets
|
||||
this.getPlayer().sendPacket(new PacketAskAddFriendRsp(targetUid));
|
||||
}
|
||||
|
||||
/** Gets total amount of potential friends
|
||||
* */
|
||||
public int getFullFriendCount() {
|
||||
return this.getPendingFriends().size() + this.getFriends().size();
|
||||
}
|
||||
public synchronized boolean isFriendsWith(int uid) {
|
||||
return this.getFriends().containsKey(uid);
|
||||
}
|
||||
|
||||
public synchronized void loadFromDatabase() {
|
||||
if (this.hasLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get friendships from the db
|
||||
List<Friendship> friendships = DatabaseHelper.getFriends(player);
|
||||
friendships.forEach(this::loadFriendFromDatabase);
|
||||
|
||||
// Set loaded flag
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
private void loadFriendFromDatabase(Friendship friendship) {
|
||||
// Set friendship owner
|
||||
friendship.setOwner(getPlayer());
|
||||
private synchronized Friendship getFriendshipById(int id) {
|
||||
Friendship friendship = this.getFriends().get(id);
|
||||
if (friendship == null) {
|
||||
friendship = this.getPendingFriendById(id);
|
||||
}
|
||||
return friendship;
|
||||
}
|
||||
|
||||
// Check if friend is online
|
||||
Player friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid());
|
||||
if (friend != null) {
|
||||
// Set friend to online mode
|
||||
friendship.setFriendProfile(friend);
|
||||
|
||||
// Update our status on friend's client if theyre online
|
||||
if (friend.getFriendsList().hasLoaded()) {
|
||||
Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getUid());
|
||||
if (theirFriendship != null) {
|
||||
// Update friend profile
|
||||
theirFriendship.setFriendProfile(getPlayer());
|
||||
} else {
|
||||
// They dont have us on their friends list anymore, rip
|
||||
friendship.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, load to our friends list
|
||||
if (friendship.isFriend()) {
|
||||
getFriends().put(friendship.getFriendId(), friendship);
|
||||
} else {
|
||||
getPendingFriends().put(friendship.getFriendId(), friendship);
|
||||
// TODO - Hacky fix to force client to see a notification for a friendship
|
||||
if (getPendingFriends().size() == 1) {
|
||||
getPlayer().getSession().send(new PacketAskAddFriendNotify(friendship));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
// Update all our friends
|
||||
List<Friendship> friendships = DatabaseHelper.getReverseFriends(getPlayer());
|
||||
for (Friendship friend : friendships) {
|
||||
friend.setFriendProfile(this.getPlayer());
|
||||
friend.save();
|
||||
}
|
||||
}
|
||||
private synchronized Friendship getFriendById(int id) {
|
||||
return this.getFriends().get(id);
|
||||
}
|
||||
|
||||
private synchronized Friendship getPendingFriendById(int id) {
|
||||
return this.getPendingFriends().get(id);
|
||||
}
|
||||
|
||||
public void addFriend(Friendship friendship) {
|
||||
getFriends().put(friendship.getFriendId(), friendship);
|
||||
}
|
||||
|
||||
public void addPendingFriend(Friendship friendship) {
|
||||
getPendingFriends().put(friendship.getFriendId(), friendship);
|
||||
}
|
||||
|
||||
public synchronized void handleFriendRequest(int targetUid, DealAddFriendResultType result) {
|
||||
// Check if player has sent friend request
|
||||
Friendship myFriendship = this.getPendingFriendById(targetUid);
|
||||
if (myFriendship == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure asker cant do anything
|
||||
if (myFriendship.getAskerId() == this.getPlayer().getUid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
|
||||
if (target == null) {
|
||||
return; // Should never happen
|
||||
}
|
||||
|
||||
// Get target's friendship
|
||||
Friendship theirFriendship = null;
|
||||
if (target.isOnline()) {
|
||||
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
|
||||
} else {
|
||||
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
|
||||
}
|
||||
|
||||
if (theirFriendship == null) {
|
||||
// They dont have us on their friends list anymore, rip
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
myFriendship.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle
|
||||
if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT) { // Request accepted
|
||||
myFriendship.setIsFriend(true);
|
||||
theirFriendship.setIsFriend(true);
|
||||
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
this.addFriend(myFriendship);
|
||||
|
||||
if (target.isOnline()) {
|
||||
target.getFriendsList().getPendingFriends().remove(this.getPlayer().getUid());
|
||||
target.getFriendsList().addFriend(theirFriendship);
|
||||
}
|
||||
|
||||
myFriendship.save();
|
||||
theirFriendship.save();
|
||||
} else { // Request declined
|
||||
// Delete from my pending friends
|
||||
this.getPendingFriends().remove(myFriendship.getOwnerId());
|
||||
myFriendship.delete();
|
||||
// Delete from target uid
|
||||
if (target.isOnline()) {
|
||||
theirFriendship = target.getFriendsList().getPendingFriendById(this.getPlayer().getUid());
|
||||
}
|
||||
theirFriendship.delete();
|
||||
}
|
||||
|
||||
// Packet
|
||||
this.getPlayer().sendPacket(new PacketDealAddFriendRsp(targetUid, result));
|
||||
}
|
||||
|
||||
public synchronized void deleteFriend(int targetUid) {
|
||||
Friendship myFriendship = this.getFriendById(targetUid);
|
||||
if (myFriendship == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getFriends().remove(targetUid);
|
||||
myFriendship.delete();
|
||||
|
||||
Friendship theirFriendship = null;
|
||||
Player friend = myFriendship.getFriendProfile().getPlayer();
|
||||
if (friend != null) {
|
||||
// Friend online
|
||||
theirFriendship = friend.getFriendsList().getFriendById(this.getPlayer().getUid());
|
||||
if (theirFriendship != null) {
|
||||
friend.getFriendsList().getFriends().remove(theirFriendship.getFriendId());
|
||||
theirFriendship.delete();
|
||||
friend.sendPacket(new PacketDeleteFriendNotify(theirFriendship.getFriendId()));
|
||||
}
|
||||
} else {
|
||||
// Friend offline
|
||||
theirFriendship = DatabaseHelper.getReverseFriendship(myFriendship);
|
||||
if (theirFriendship != null) {
|
||||
theirFriendship.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Packet
|
||||
this.getPlayer().sendPacket(new PacketDeleteFriendRsp(targetUid));
|
||||
}
|
||||
|
||||
public synchronized void sendFriendRequest(int targetUid) {
|
||||
Player target = getPlayer().getSession().getServer().getPlayerByUid(targetUid, true);
|
||||
|
||||
if (target == null || target == this.getPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if friend already exists
|
||||
if (this.getPendingFriends().containsKey(targetUid) || this.getFriends().containsKey(targetUid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create friendships
|
||||
Friendship myFriendship = new Friendship(getPlayer(), target, getPlayer());
|
||||
Friendship theirFriendship = new Friendship(target, getPlayer(), getPlayer());
|
||||
|
||||
// Add pending lists
|
||||
this.addPendingFriend(myFriendship);
|
||||
|
||||
if (target.isOnline() && target.getFriendsList().hasLoaded()) {
|
||||
target.getFriendsList().addPendingFriend(theirFriendship);
|
||||
target.sendPacket(new PacketAskAddFriendNotify(theirFriendship));
|
||||
}
|
||||
|
||||
// Save
|
||||
myFriendship.save();
|
||||
theirFriendship.save();
|
||||
|
||||
// Packets
|
||||
this.getPlayer().sendPacket(new PacketAskAddFriendRsp(targetUid));
|
||||
}
|
||||
|
||||
/** Gets total amount of potential friends
|
||||
* */
|
||||
public int getFullFriendCount() {
|
||||
return this.getPendingFriends().size() + this.getFriends().size();
|
||||
}
|
||||
|
||||
public synchronized void loadFromDatabase() {
|
||||
if (this.hasLoaded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get friendships from the db
|
||||
List<Friendship> friendships = DatabaseHelper.getFriends(player);
|
||||
friendships.forEach(this::loadFriendFromDatabase);
|
||||
|
||||
// Set loaded flag
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
private void loadFriendFromDatabase(Friendship friendship) {
|
||||
// Set friendship owner
|
||||
friendship.setOwner(getPlayer());
|
||||
|
||||
// Check if friend is online
|
||||
Player friend = getPlayer().getSession().getServer().getPlayerByUid(friendship.getFriendProfile().getUid());
|
||||
if (friend != null) {
|
||||
// Set friend to online mode
|
||||
friendship.setFriendProfile(friend);
|
||||
|
||||
// Update our status on friend's client if theyre online
|
||||
if (friend.getFriendsList().hasLoaded()) {
|
||||
Friendship theirFriendship = friend.getFriendsList().getFriendshipById(getPlayer().getUid());
|
||||
if (theirFriendship != null) {
|
||||
// Update friend profile
|
||||
theirFriendship.setFriendProfile(getPlayer());
|
||||
} else {
|
||||
// They dont have us on their friends list anymore, rip
|
||||
friendship.delete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, load to our friends list
|
||||
if (friendship.isFriend()) {
|
||||
getFriends().put(friendship.getFriendId(), friendship);
|
||||
} else {
|
||||
getPendingFriends().put(friendship.getFriendId(), friendship);
|
||||
// TODO - Hacky fix to force client to see a notification for a friendship
|
||||
if (getPendingFriends().size() == 1) {
|
||||
getPlayer().getSession().send(new PacketAskAddFriendNotify(friendship));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
// Update all our friends
|
||||
List<Friendship> friendships = DatabaseHelper.getReverseFriends(getPlayer());
|
||||
for (Friendship friend : friendships) {
|
||||
friend.setFriendProfile(this.getPlayer());
|
||||
friend.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,164 +10,164 @@ import emu.grasscutter.utils.Utils;
|
||||
import lombok.Getter;
|
||||
|
||||
public class GachaBanner {
|
||||
@Getter private int gachaType;
|
||||
@Getter private int scheduleId;
|
||||
@Getter private String prefabPath;
|
||||
@Getter private String previewPrefabPath;
|
||||
@Getter private String titlePath;
|
||||
private int costItemId = 0;
|
||||
private int costItemAmount = 1;
|
||||
private int costItemId10 = 0;
|
||||
private int costItemAmount10 = 10;
|
||||
@Getter private int beginTime;
|
||||
@Getter private int endTime;
|
||||
@Getter private int sortId;
|
||||
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
|
||||
private int[] rateUpItems4 = {};
|
||||
private int[] rateUpItems5 = {};
|
||||
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
|
||||
@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
|
||||
@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
||||
@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
|
||||
@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
||||
@Getter private boolean removeC6FromPool = false;
|
||||
@Getter private boolean autoStripRateUpFromFallback = true;
|
||||
private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
|
||||
private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
|
||||
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
|
||||
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
|
||||
private int eventChance4 = 50; // Chance to win a featured event item
|
||||
private int eventChance5 = 50; // Chance to win a featured event item
|
||||
@Getter private BannerType bannerType = BannerType.STANDARD;
|
||||
@Getter private int gachaType;
|
||||
@Getter private int scheduleId;
|
||||
@Getter private String prefabPath;
|
||||
@Getter private String previewPrefabPath;
|
||||
@Getter private String titlePath;
|
||||
private int costItemId = 0;
|
||||
private int costItemAmount = 1;
|
||||
private int costItemId10 = 0;
|
||||
private int costItemAmount10 = 10;
|
||||
@Getter private int beginTime;
|
||||
@Getter private int endTime;
|
||||
@Getter private int sortId;
|
||||
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
|
||||
private int[] rateUpItems4 = {};
|
||||
private int[] rateUpItems5 = {};
|
||||
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
|
||||
@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
|
||||
@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
||||
@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
|
||||
@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
||||
@Getter private boolean removeC6FromPool = false;
|
||||
@Getter private boolean autoStripRateUpFromFallback = true;
|
||||
private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
|
||||
private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
|
||||
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
|
||||
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
|
||||
private int eventChance4 = 50; // Chance to win a featured event item
|
||||
private int eventChance5 = 50; // Chance to win a featured event item
|
||||
@Getter private BannerType bannerType = BannerType.STANDARD;
|
||||
|
||||
// Kinda wanna deprecate these but they're in people's configs
|
||||
private int[] rateUpItems1 = {};
|
||||
private int[] rateUpItems2 = {};
|
||||
private int eventChance = -1;
|
||||
private int costItem = 0;
|
||||
@Getter private int wishMaxProgress = 2;
|
||||
// Kinda wanna deprecate these but they're in people's configs
|
||||
private int[] rateUpItems1 = {};
|
||||
private int[] rateUpItems2 = {};
|
||||
private int eventChance = -1;
|
||||
private int costItem = 0;
|
||||
@Getter private int wishMaxProgress = 2;
|
||||
|
||||
public ItemParamData getCost(int numRolls) {
|
||||
return switch (numRolls) {
|
||||
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
|
||||
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
|
||||
};
|
||||
}
|
||||
public ItemParamData getCost(int numRolls) {
|
||||
return switch (numRolls) {
|
||||
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
|
||||
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
|
||||
};
|
||||
}
|
||||
|
||||
public int getCostItem() {
|
||||
return (costItem > 0) ? costItem : costItemId;
|
||||
}
|
||||
public int getCostItem() {
|
||||
return (costItem > 0) ? costItem : costItemId;
|
||||
}
|
||||
|
||||
public int[] getRateUpItems4() {
|
||||
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
|
||||
}
|
||||
public int[] getRateUpItems5() {
|
||||
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
|
||||
}
|
||||
public int[] getRateUpItems4() {
|
||||
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
|
||||
}
|
||||
public int[] getRateUpItems5() {
|
||||
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
|
||||
}
|
||||
|
||||
public boolean hasEpitomized() {
|
||||
return bannerType.equals(BannerType.WEAPON);
|
||||
}
|
||||
public boolean hasEpitomized() {
|
||||
return bannerType.equals(BannerType.WEAPON);
|
||||
}
|
||||
|
||||
public int getWeight(int rarity, int pity) {
|
||||
return switch(rarity) {
|
||||
case 4 -> Utils.lerp(pity, weights4);
|
||||
default -> Utils.lerp(pity, weights5);
|
||||
};
|
||||
}
|
||||
public int getWeight(int rarity, int pity) {
|
||||
return switch (rarity) {
|
||||
case 4 -> Utils.lerp(pity, weights4);
|
||||
default -> Utils.lerp(pity, weights5);
|
||||
};
|
||||
}
|
||||
|
||||
public int getPoolBalanceWeight(int rarity, int pity) {
|
||||
return switch(rarity) {
|
||||
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
|
||||
default -> Utils.lerp(pity, poolBalanceWeights5);
|
||||
};
|
||||
}
|
||||
public int getPoolBalanceWeight(int rarity, int pity) {
|
||||
return switch (rarity) {
|
||||
case 4 -> Utils.lerp(pity, poolBalanceWeights4);
|
||||
default -> Utils.lerp(pity, poolBalanceWeights5);
|
||||
};
|
||||
}
|
||||
|
||||
public int getEventChance(int rarity) {
|
||||
return switch(rarity) {
|
||||
case 4 -> eventChance4;
|
||||
default -> (eventChance > -1) ? eventChance : eventChance5;
|
||||
};
|
||||
}
|
||||
|
||||
public GachaInfo toProto(Player player) {
|
||||
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
|
||||
String sessionKey = player.getAccount().getSessionKey();
|
||||
|
||||
String record = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
|
||||
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
|
||||
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
|
||||
+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
|
||||
String details = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
|
||||
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
|
||||
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
|
||||
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
|
||||
public int getEventChance(int rarity) {
|
||||
return switch (rarity) {
|
||||
case 4 -> eventChance4;
|
||||
default -> (eventChance > -1) ? eventChance : eventChance5;
|
||||
};
|
||||
}
|
||||
|
||||
// Grasscutter.getLogger().info("record = " + record);
|
||||
ItemParamData costItem1 = this.getCost(1);
|
||||
ItemParamData costItem10 = this.getCost(10);
|
||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
|
||||
int leftGachaTimes = switch(gachaTimesLimit) {
|
||||
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
|
||||
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
|
||||
};
|
||||
GachaInfo.Builder info = GachaInfo.newBuilder()
|
||||
.setGachaType(this.getGachaType())
|
||||
.setScheduleId(this.getScheduleId())
|
||||
.setBeginTime(this.getBeginTime())
|
||||
.setEndTime(this.getEndTime())
|
||||
.setCostItemId(costItem1.getId())
|
||||
.setCostItemNum(costItem1.getCount())
|
||||
.setTenCostItemId(costItem10.getId())
|
||||
.setTenCostItemNum(costItem10.getCount())
|
||||
.setGachaPrefabPath(this.getPrefabPath())
|
||||
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
|
||||
.setGachaProbUrl(details)
|
||||
.setGachaProbUrlOversea(details)
|
||||
.setGachaRecordUrl(record)
|
||||
.setGachaRecordUrlOversea(record)
|
||||
.setLeftGachaTimes(leftGachaTimes)
|
||||
.setGachaTimesLimit(gachaTimesLimit)
|
||||
.setGachaSortId(this.getSortId());
|
||||
public GachaInfo toProto(Player player) {
|
||||
// TODO: use other Nonce/key insteadof session key to ensure the overall security for the player
|
||||
String sessionKey = player.getAccount().getSessionKey();
|
||||
|
||||
if(hasEpitomized()) {
|
||||
info.setWishItemId(gachaInfo.getWishItemId())
|
||||
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
|
||||
.setWishMaxProgress(this.getWishMaxProgress());
|
||||
}
|
||||
String record = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
|
||||
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
|
||||
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
|
||||
+ "/gacha?s=" + sessionKey + "&gachaType=" + gachaType;
|
||||
String details = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://"
|
||||
+ lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":"
|
||||
+ lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort)
|
||||
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
|
||||
|
||||
if (this.getTitlePath() != null) {
|
||||
info.setTitleTextmap(this.getTitlePath());
|
||||
}
|
||||
|
||||
if (this.getRateUpItems5().length > 0) {
|
||||
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
|
||||
|
||||
for (int id : getRateUpItems5()) {
|
||||
upInfo.addItemIdList(id);
|
||||
info.addDisplayUp5ItemList(id);
|
||||
}
|
||||
|
||||
info.addGachaUpInfoList(upInfo);
|
||||
}
|
||||
|
||||
if (this.getRateUpItems4().length > 0) {
|
||||
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
|
||||
|
||||
for (int id : getRateUpItems4()) {
|
||||
upInfo.addItemIdList(id);
|
||||
if (info.getDisplayUp4ItemListCount() == 0) {
|
||||
info.addDisplayUp4ItemList(id);
|
||||
}
|
||||
}
|
||||
|
||||
info.addGachaUpInfoList(upInfo);
|
||||
}
|
||||
|
||||
return info.build();
|
||||
}
|
||||
|
||||
public enum BannerType {
|
||||
STANDARD, EVENT, WEAPON;
|
||||
}
|
||||
// Grasscutter.getLogger().info("record = " + record);
|
||||
ItemParamData costItem1 = this.getCost(1);
|
||||
ItemParamData costItem10 = this.getCost(10);
|
||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
|
||||
int leftGachaTimes = switch (gachaTimesLimit) {
|
||||
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
|
||||
default -> Math.max(gachaTimesLimit - gachaInfo.getTotalPulls(), 0);
|
||||
};
|
||||
GachaInfo.Builder info = GachaInfo.newBuilder()
|
||||
.setGachaType(this.getGachaType())
|
||||
.setScheduleId(this.getScheduleId())
|
||||
.setBeginTime(this.getBeginTime())
|
||||
.setEndTime(this.getEndTime())
|
||||
.setCostItemId(costItem1.getId())
|
||||
.setCostItemNum(costItem1.getCount())
|
||||
.setTenCostItemId(costItem10.getId())
|
||||
.setTenCostItemNum(costItem10.getCount())
|
||||
.setGachaPrefabPath(this.getPrefabPath())
|
||||
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
|
||||
.setGachaProbUrl(details)
|
||||
.setGachaProbUrlOversea(details)
|
||||
.setGachaRecordUrl(record)
|
||||
.setGachaRecordUrlOversea(record)
|
||||
.setLeftGachaTimes(leftGachaTimes)
|
||||
.setGachaTimesLimit(gachaTimesLimit)
|
||||
.setGachaSortId(this.getSortId());
|
||||
|
||||
if (hasEpitomized()) {
|
||||
info.setWishItemId(gachaInfo.getWishItemId())
|
||||
.setWishProgress(gachaInfo.getFailedChosenItemPulls())
|
||||
.setWishMaxProgress(this.getWishMaxProgress());
|
||||
}
|
||||
|
||||
if (this.getTitlePath() != null) {
|
||||
info.setTitleTextmap(this.getTitlePath());
|
||||
}
|
||||
|
||||
if (this.getRateUpItems5().length > 0) {
|
||||
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1);
|
||||
|
||||
for (int id : getRateUpItems5()) {
|
||||
upInfo.addItemIdList(id);
|
||||
info.addDisplayUp5ItemList(id);
|
||||
}
|
||||
|
||||
info.addGachaUpInfoList(upInfo);
|
||||
}
|
||||
|
||||
if (this.getRateUpItems4().length > 0) {
|
||||
GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2);
|
||||
|
||||
for (int id : getRateUpItems4()) {
|
||||
upInfo.addItemIdList(id);
|
||||
if (info.getDisplayUp4ItemListCount() == 0) {
|
||||
info.addDisplayUp4ItemList(id);
|
||||
}
|
||||
}
|
||||
|
||||
info.addGachaUpInfoList(upInfo);
|
||||
}
|
||||
|
||||
return info.build();
|
||||
}
|
||||
|
||||
public enum BannerType {
|
||||
STANDARD, EVENT, WEAPON;
|
||||
}
|
||||
}
|
||||
|
@ -48,400 +48,400 @@ import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
|
||||
public class GachaSystem extends BaseGameSystem {
|
||||
private final Int2ObjectMap<GachaBanner> gachaBanners;
|
||||
private WatchService watchService;
|
||||
private final Int2ObjectMap<GachaBanner> gachaBanners;
|
||||
private WatchService watchService;
|
||||
|
||||
private static final int starglitterId = 221;
|
||||
private static final int stardustId = 222;
|
||||
private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
||||
private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
||||
private static final int starglitterId = 221;
|
||||
private static final int stardustId = 222;
|
||||
private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
||||
private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
||||
|
||||
public GachaSystem(GameServer server) {
|
||||
super(server);
|
||||
this.gachaBanners = new Int2ObjectOpenHashMap<>();
|
||||
this.load();
|
||||
this.startWatcher(server);
|
||||
}
|
||||
public GachaSystem(GameServer server) {
|
||||
super(server);
|
||||
this.gachaBanners = new Int2ObjectOpenHashMap<>();
|
||||
this.load();
|
||||
this.startWatcher(server);
|
||||
}
|
||||
|
||||
public Int2ObjectMap<GachaBanner> getGachaBanners() {
|
||||
return gachaBanners;
|
||||
}
|
||||
public Int2ObjectMap<GachaBanner> getGachaBanners() {
|
||||
return gachaBanners;
|
||||
}
|
||||
|
||||
public int randomRange(int min, int max) { // Both are inclusive
|
||||
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
|
||||
}
|
||||
public int randomRange(int min, int max) { // Both are inclusive
|
||||
return ThreadLocalRandom.current().nextInt(max - min + 1) + min;
|
||||
}
|
||||
|
||||
public int getRandom(int[] array) {
|
||||
return array[randomRange(0, array.length - 1)];
|
||||
}
|
||||
public int getRandom(int[] array) {
|
||||
return array[randomRange(0, array.length - 1)];
|
||||
}
|
||||
|
||||
public synchronized void load() {
|
||||
try (Reader fileReader = DataLoader.loadReader("Banners.json")) {
|
||||
getGachaBanners().clear();
|
||||
List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType());
|
||||
if(banners.size() > 0) {
|
||||
for (GachaBanner banner : banners) {
|
||||
getGachaBanners().put(banner.getScheduleId(), banner);
|
||||
}
|
||||
Grasscutter.getLogger().debug("Banners successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load banners. Banners size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public synchronized void load() {
|
||||
try (Reader fileReader = DataLoader.loadReader("Banners.json")) {
|
||||
getGachaBanners().clear();
|
||||
List<GachaBanner> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType());
|
||||
if (banners.size() > 0) {
|
||||
for (GachaBanner banner : banners) {
|
||||
getGachaBanners().put(banner.getScheduleId(), banner);
|
||||
}
|
||||
Grasscutter.getLogger().debug("Banners successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load banners. Banners size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private class BannerPools {
|
||||
public int[] rateUpItems4;
|
||||
public int[] rateUpItems5;
|
||||
public int[] fallbackItems4Pool1;
|
||||
public int[] fallbackItems4Pool2;
|
||||
public int[] fallbackItems5Pool1;
|
||||
public int[] fallbackItems5Pool2;
|
||||
private class BannerPools {
|
||||
public int[] rateUpItems4;
|
||||
public int[] rateUpItems5;
|
||||
public int[] fallbackItems4Pool1;
|
||||
public int[] fallbackItems4Pool2;
|
||||
public int[] fallbackItems5Pool1;
|
||||
public int[] fallbackItems5Pool2;
|
||||
|
||||
public BannerPools(GachaBanner banner) {
|
||||
rateUpItems4 = banner.getRateUpItems4();
|
||||
rateUpItems5 = banner.getRateUpItems5();
|
||||
fallbackItems4Pool1 = banner.getFallbackItems4Pool1();
|
||||
fallbackItems4Pool2 = banner.getFallbackItems4Pool2();
|
||||
fallbackItems5Pool1 = banner.getFallbackItems5Pool1();
|
||||
fallbackItems5Pool2 = banner.getFallbackItems5Pool2();
|
||||
public BannerPools(GachaBanner banner) {
|
||||
rateUpItems4 = banner.getRateUpItems4();
|
||||
rateUpItems5 = banner.getRateUpItems5();
|
||||
fallbackItems4Pool1 = banner.getFallbackItems4Pool1();
|
||||
fallbackItems4Pool2 = banner.getFallbackItems4Pool2();
|
||||
fallbackItems5Pool1 = banner.getFallbackItems5Pool1();
|
||||
fallbackItems5Pool2 = banner.getFallbackItems5Pool2();
|
||||
|
||||
if (banner.isAutoStripRateUpFromFallback()) {
|
||||
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4);
|
||||
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4);
|
||||
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5);
|
||||
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, rateUpItems5);
|
||||
}
|
||||
}
|
||||
if (banner.isAutoStripRateUpFromFallback()) {
|
||||
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4);
|
||||
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4);
|
||||
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5);
|
||||
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, rateUpItems5);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFromAllPools(int[] itemIds) {
|
||||
rateUpItems4 = Utils.setSubtract(rateUpItems4, itemIds);
|
||||
rateUpItems5 = Utils.setSubtract(rateUpItems5, itemIds);
|
||||
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, itemIds);
|
||||
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, itemIds);
|
||||
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, itemIds);
|
||||
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, itemIds);
|
||||
}
|
||||
}
|
||||
public void removeFromAllPools(int[] itemIds) {
|
||||
rateUpItems4 = Utils.setSubtract(rateUpItems4, itemIds);
|
||||
rateUpItems5 = Utils.setSubtract(rateUpItems5, itemIds);
|
||||
fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, itemIds);
|
||||
fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, itemIds);
|
||||
fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, itemIds);
|
||||
fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, itemIds);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class?
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)){
|
||||
return -2; // Not an Avatar
|
||||
}
|
||||
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
|
||||
if (avatar == null) {
|
||||
return -1; // Doesn't have
|
||||
}
|
||||
// Constellation
|
||||
int constLevel = avatar.getCoreProudSkillLevel();
|
||||
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
|
||||
constLevel += (constItem == null)? 0 : constItem.getCount();
|
||||
return constLevel;
|
||||
}
|
||||
private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class?
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)) {
|
||||
return -2; // Not an Avatar
|
||||
}
|
||||
Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000);
|
||||
if (avatar == null) {
|
||||
return -1; // Doesn't have
|
||||
}
|
||||
// Constellation
|
||||
int constLevel = avatar.getCoreProudSkillLevel();
|
||||
GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100);
|
||||
constLevel += (constItem == null)? 0 : constItem.getCount();
|
||||
return constLevel;
|
||||
}
|
||||
|
||||
private synchronized int[] removeC6FromPool(int[] itemPool, Player player) {
|
||||
IntList temp = new IntArrayList();
|
||||
for (int itemId : itemPool) {
|
||||
if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
|
||||
temp.add(itemId);
|
||||
}
|
||||
}
|
||||
return temp.toIntArray();
|
||||
}
|
||||
private synchronized int[] removeC6FromPool(int[] itemPool, Player player) {
|
||||
IntList temp = new IntArrayList();
|
||||
for (int itemId : itemPool) {
|
||||
if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) {
|
||||
temp.add(itemId);
|
||||
}
|
||||
}
|
||||
return temp.toIntArray();
|
||||
}
|
||||
|
||||
private synchronized int drawRoulette(int[] weights, int cutoff) {
|
||||
// This follows the logic laid out in issue #183
|
||||
// Simple weighted selection with an upper bound for the roll that cuts off trailing entries
|
||||
// All weights must be >= 0
|
||||
int total = 0;
|
||||
for (int weight : weights) {
|
||||
if (weight < 0) {
|
||||
throw new IllegalArgumentException("Weights must be non-negative!");
|
||||
}
|
||||
total += weight;
|
||||
}
|
||||
int roll = ThreadLocalRandom.current().nextInt((total < cutoff)? total : cutoff);
|
||||
int subTotal = 0;
|
||||
for (int i=0; i<weights.length; i++) {
|
||||
subTotal += weights[i];
|
||||
if (roll < subTotal) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// throw new IllegalStateException();
|
||||
return 0; // This should only be reachable if total==0
|
||||
}
|
||||
private synchronized int drawRoulette(int[] weights, int cutoff) {
|
||||
// This follows the logic laid out in issue #183
|
||||
// Simple weighted selection with an upper bound for the roll that cuts off trailing entries
|
||||
// All weights must be >= 0
|
||||
int total = 0;
|
||||
for (int weight : weights) {
|
||||
if (weight < 0) {
|
||||
throw new IllegalArgumentException("Weights must be non-negative!");
|
||||
}
|
||||
total += weight;
|
||||
}
|
||||
int roll = ThreadLocalRandom.current().nextInt((total < cutoff)? total : cutoff);
|
||||
int subTotal = 0;
|
||||
for (int i=0; i<weights.length; i++) {
|
||||
subTotal += weights[i];
|
||||
if (roll < subTotal) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// throw new IllegalStateException();
|
||||
return 0; // This should only be reachable if total==0
|
||||
}
|
||||
|
||||
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
||||
if (fallback1.length < 1) {
|
||||
if (fallback2.length < 1) {
|
||||
return getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default);
|
||||
} else {
|
||||
return getRandom(fallback2);
|
||||
}
|
||||
} else if (fallback2.length < 1) {
|
||||
return getRandom(fallback1);
|
||||
} else { // Both pools are possible, use the pool balancer
|
||||
int pityPool1 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 1));
|
||||
int pityPool2 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 2));
|
||||
int chosenPool = switch ((pityPool1 >= pityPool2)? 1 : 0) { // Larger weight must come first for the hard cutoff to function correctly
|
||||
case 1 -> 1 + drawRoulette(new int[] {pityPool1, pityPool2}, 10000);
|
||||
default -> 2 - drawRoulette(new int[] {pityPool2, pityPool1}, 10000);
|
||||
};
|
||||
return switch (chosenPool) {
|
||||
case 1:
|
||||
gachaInfo.setPityPool(rarity, 1, 0);
|
||||
yield getRandom(fallback1);
|
||||
default:
|
||||
gachaInfo.setPityPool(rarity, 2, 0);
|
||||
yield getRandom(fallback2);
|
||||
};
|
||||
}
|
||||
}
|
||||
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
||||
if (fallback1.length < 1) {
|
||||
if (fallback2.length < 1) {
|
||||
return getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default);
|
||||
} else {
|
||||
return getRandom(fallback2);
|
||||
}
|
||||
} else if (fallback2.length < 1) {
|
||||
return getRandom(fallback1);
|
||||
} else { // Both pools are possible, use the pool balancer
|
||||
int pityPool1 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 1));
|
||||
int pityPool2 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 2));
|
||||
int chosenPool = switch ((pityPool1 >= pityPool2)? 1 : 0) { // Larger weight must come first for the hard cutoff to function correctly
|
||||
case 1 -> 1 + drawRoulette(new int[] {pityPool1, pityPool2}, 10000);
|
||||
default -> 2 - drawRoulette(new int[] {pityPool2, pityPool1}, 10000);
|
||||
};
|
||||
return switch (chosenPool) {
|
||||
case 1:
|
||||
gachaInfo.setPityPool(rarity, 1, 0);
|
||||
yield getRandom(fallback1);
|
||||
default:
|
||||
gachaInfo.setPityPool(rarity, 2, 0);
|
||||
yield getRandom(fallback2);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized int doRarePull(int[] featured, int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
||||
int itemId = 0;
|
||||
boolean epitomized = (banner.hasEpitomized()) && (rarity == 5) && (gachaInfo.getWishItemId() != 0);
|
||||
boolean pityEpitomized = (gachaInfo.getFailedChosenItemPulls() >= banner.getWishMaxProgress()); // Maximum fate points reached
|
||||
boolean pityFeatured = (gachaInfo.getFailedFeaturedItemPulls(rarity) >= 1); // Lost previous coinflip
|
||||
boolean rollFeatured = (this.randomRange(1, 100) <= banner.getEventChance(rarity)); // Won this coinflip
|
||||
boolean pullFeatured = pityFeatured || rollFeatured;
|
||||
private synchronized int doRarePull(int[] featured, int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
||||
int itemId = 0;
|
||||
boolean epitomized = (banner.hasEpitomized()) && (rarity == 5) && (gachaInfo.getWishItemId() != 0);
|
||||
boolean pityEpitomized = (gachaInfo.getFailedChosenItemPulls() >= banner.getWishMaxProgress()); // Maximum fate points reached
|
||||
boolean pityFeatured = (gachaInfo.getFailedFeaturedItemPulls(rarity) >= 1); // Lost previous coinflip
|
||||
boolean rollFeatured = (this.randomRange(1, 100) <= banner.getEventChance(rarity)); // Won this coinflip
|
||||
boolean pullFeatured = pityFeatured || rollFeatured;
|
||||
|
||||
if (epitomized && pityEpitomized) { // Auto pick item when epitomized points reached
|
||||
gachaInfo.setFailedFeaturedItemPulls(rarity, 0); // Epitomized item will always be a featured one
|
||||
itemId = gachaInfo.getWishItemId();
|
||||
} else {
|
||||
if (pullFeatured && (featured.length > 0)) {
|
||||
gachaInfo.setFailedFeaturedItemPulls(rarity, 0);
|
||||
itemId = getRandom(featured);
|
||||
} else {
|
||||
gachaInfo.addFailedFeaturedItemPulls(rarity, 1); // This could be moved into doFallbackRarePull but having it here makes it clearer
|
||||
itemId = doFallbackRarePull(fallback1, fallback2, rarity, banner, gachaInfo);
|
||||
}
|
||||
}
|
||||
if (epitomized && pityEpitomized) { // Auto pick item when epitomized points reached
|
||||
gachaInfo.setFailedFeaturedItemPulls(rarity, 0); // Epitomized item will always be a featured one
|
||||
itemId = gachaInfo.getWishItemId();
|
||||
} else {
|
||||
if (pullFeatured && (featured.length > 0)) {
|
||||
gachaInfo.setFailedFeaturedItemPulls(rarity, 0);
|
||||
itemId = getRandom(featured);
|
||||
} else {
|
||||
gachaInfo.addFailedFeaturedItemPulls(rarity, 1); // This could be moved into doFallbackRarePull but having it here makes it clearer
|
||||
itemId = doFallbackRarePull(fallback1, fallback2, rarity, banner, gachaInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (epitomized) {
|
||||
if(itemId == gachaInfo.getWishItemId()) { // Reset epitomized points when got wished item
|
||||
gachaInfo.setFailedChosenItemPulls(0);
|
||||
} else { // Add epitomized points if not get wished item
|
||||
gachaInfo.addFailedChosenItemPulls(1);
|
||||
}
|
||||
}
|
||||
return itemId;
|
||||
}
|
||||
if (epitomized) {
|
||||
if (itemId == gachaInfo.getWishItemId()) { // Reset epitomized points when got wished item
|
||||
gachaInfo.setFailedChosenItemPulls(0);
|
||||
} else { // Add epitomized points if not get wished item
|
||||
gachaInfo.addFailedChosenItemPulls(1);
|
||||
}
|
||||
}
|
||||
return itemId;
|
||||
}
|
||||
|
||||
private synchronized int doPull(GachaBanner banner, PlayerGachaBannerInfo gachaInfo, BannerPools pools) {
|
||||
// Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity)
|
||||
gachaInfo.incPityAll();
|
||||
private synchronized int doPull(GachaBanner banner, PlayerGachaBannerInfo gachaInfo, BannerPools pools) {
|
||||
// Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity)
|
||||
gachaInfo.incPityAll();
|
||||
|
||||
int[] weights = {banner.getWeight(5, gachaInfo.getPity5()), banner.getWeight(4, gachaInfo.getPity4()), 10000};
|
||||
int levelWon = 5 - drawRoulette(weights, 10000);
|
||||
int[] weights = {banner.getWeight(5, gachaInfo.getPity5()), banner.getWeight(4, gachaInfo.getPity4()), 10000};
|
||||
int levelWon = 5 - drawRoulette(weights, 10000);
|
||||
|
||||
return switch (levelWon) {
|
||||
case 5:
|
||||
gachaInfo.setPity5(0);
|
||||
yield doRarePull(pools.rateUpItems5, pools.fallbackItems5Pool1, pools.fallbackItems5Pool2, 5, banner, gachaInfo);
|
||||
case 4:
|
||||
gachaInfo.setPity4(0);
|
||||
yield doRarePull(pools.rateUpItems4, pools.fallbackItems4Pool1, pools.fallbackItems4Pool2, 4, banner, gachaInfo);
|
||||
default:
|
||||
yield getRandom(banner.getFallbackItems3());
|
||||
};
|
||||
}
|
||||
return switch (levelWon) {
|
||||
case 5:
|
||||
gachaInfo.setPity5(0);
|
||||
yield doRarePull(pools.rateUpItems5, pools.fallbackItems5Pool1, pools.fallbackItems5Pool2, 5, banner, gachaInfo);
|
||||
case 4:
|
||||
gachaInfo.setPity4(0);
|
||||
yield doRarePull(pools.rateUpItems4, pools.fallbackItems4Pool1, pools.fallbackItems4Pool2, 4, banner, gachaInfo);
|
||||
default:
|
||||
yield getRandom(banner.getFallbackItems3());
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized void doPulls(Player player, int scheduleId, int times) {
|
||||
// Sanity check
|
||||
if (times != 10 && times != 1) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_INVALID_TIMES));
|
||||
return;
|
||||
}
|
||||
Inventory inventory = player.getInventory();
|
||||
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_ITEM_EXCEED_LIMIT));
|
||||
return;
|
||||
}
|
||||
public synchronized void doPulls(Player player, int scheduleId, int times) {
|
||||
// Sanity check
|
||||
if (times != 10 && times != 1) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_INVALID_TIMES));
|
||||
return;
|
||||
}
|
||||
Inventory inventory = player.getInventory();
|
||||
if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_ITEM_EXCEED_LIMIT));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get banner
|
||||
GachaBanner banner = this.getGachaBanners().get(scheduleId);
|
||||
if (banner == null) {
|
||||
player.sendPacket(new PacketDoGachaRsp());
|
||||
return;
|
||||
}
|
||||
// Get banner
|
||||
GachaBanner banner = this.getGachaBanners().get(scheduleId);
|
||||
if (banner == null) {
|
||||
player.sendPacket(new PacketDoGachaRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check against total limit
|
||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
|
||||
int gachaTimesLimit = banner.getGachaTimesLimit();
|
||||
if (gachaTimesLimit != Integer.MAX_VALUE && (gachaInfo.getTotalPulls() + times) > gachaTimesLimit) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_TIMES_LIMIT));
|
||||
return;
|
||||
}
|
||||
// Check against total limit
|
||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner);
|
||||
int gachaTimesLimit = banner.getGachaTimesLimit();
|
||||
if (gachaTimesLimit != Integer.MAX_VALUE && (gachaInfo.getTotalPulls() + times) > gachaTimesLimit) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_TIMES_LIMIT));
|
||||
return;
|
||||
}
|
||||
|
||||
// Spend currency
|
||||
ItemParamData cost = banner.getCost(times);
|
||||
if (cost.getCount() > 0 && !inventory.payItem(cost)) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_COST_ITEM_NOT_ENOUGH));
|
||||
return;
|
||||
}
|
||||
// Spend currency
|
||||
ItemParamData cost = banner.getCost(times);
|
||||
if (cost.getCount() > 0 && !inventory.payItem(cost)) {
|
||||
player.sendPacket(new PacketDoGachaRsp(Retcode.RET_GACHA_COST_ITEM_NOT_ENOUGH));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to character
|
||||
gachaInfo.addTotalPulls(times);
|
||||
BannerPools pools = new BannerPools(banner);
|
||||
List<GachaItem> list = new ArrayList<>();
|
||||
int stardust = 0, starglitter = 0;
|
||||
// Add to character
|
||||
gachaInfo.addTotalPulls(times);
|
||||
BannerPools pools = new BannerPools(banner);
|
||||
List<GachaItem> list = new ArrayList<>();
|
||||
int stardust = 0, starglitter = 0;
|
||||
|
||||
if (banner.isRemoveC6FromPool()) { // The ultimate form of pity (non-vanilla)
|
||||
pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player);
|
||||
pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player);
|
||||
pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player);
|
||||
pools.fallbackItems4Pool2 = removeC6FromPool(pools.fallbackItems4Pool2, player);
|
||||
pools.fallbackItems5Pool1 = removeC6FromPool(pools.fallbackItems5Pool1, player);
|
||||
pools.fallbackItems5Pool2 = removeC6FromPool(pools.fallbackItems5Pool2, player);
|
||||
}
|
||||
if (banner.isRemoveC6FromPool()) { // The ultimate form of pity (non-vanilla)
|
||||
pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player);
|
||||
pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player);
|
||||
pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player);
|
||||
pools.fallbackItems4Pool2 = removeC6FromPool(pools.fallbackItems4Pool2, player);
|
||||
pools.fallbackItems5Pool1 = removeC6FromPool(pools.fallbackItems5Pool1, player);
|
||||
pools.fallbackItems5Pool2 = removeC6FromPool(pools.fallbackItems5Pool2, player);
|
||||
}
|
||||
|
||||
for (int i = 0; i < times; i++) {
|
||||
// Roll
|
||||
int itemId = doPull(banner, gachaInfo, pools);
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if (itemData == null) {
|
||||
continue; // Maybe we should bail out if an item fails instead of rolling the rest?
|
||||
}
|
||||
for (int i = 0; i < times; i++) {
|
||||
// Roll
|
||||
int itemId = doPull(banner, gachaInfo, pools);
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if (itemData == null) {
|
||||
continue; // Maybe we should bail out if an item fails instead of rolling the rest?
|
||||
}
|
||||
|
||||
// Write gacha record
|
||||
GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), banner.getGachaType());
|
||||
DatabaseHelper.saveGachaRecord(gachaRecord);
|
||||
// Write gacha record
|
||||
GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), banner.getGachaType());
|
||||
DatabaseHelper.saveGachaRecord(gachaRecord);
|
||||
|
||||
// Create gacha item
|
||||
GachaItem.Builder gachaItem = GachaItem.newBuilder();
|
||||
int addStardust = 0, addStarglitter = 0;
|
||||
boolean isTransferItem = false;
|
||||
// Create gacha item
|
||||
GachaItem.Builder gachaItem = GachaItem.newBuilder();
|
||||
int addStardust = 0, addStarglitter = 0;
|
||||
boolean isTransferItem = false;
|
||||
|
||||
// Const check
|
||||
int constellation = checkPlayerAvatarConstellationLevel(player, itemId);
|
||||
switch (constellation) {
|
||||
case -2: // Is weapon
|
||||
switch (itemData.getRankLevel()) {
|
||||
case 5 -> addStarglitter = 10;
|
||||
case 4 -> addStarglitter = 2;
|
||||
default -> addStardust = 15;
|
||||
}
|
||||
break;
|
||||
case -1: // New character
|
||||
gachaItem.setIsGachaItemNew(true);
|
||||
break;
|
||||
default:
|
||||
if (constellation >= 6) { // C6, give consolation starglitter
|
||||
addStarglitter = (itemData.getRankLevel()==5)? 25 : 5;
|
||||
} else { // C0-C5, give constellation item
|
||||
if (banner.isRemoveC6FromPool() && constellation == 5) { // New C6, remove it from the pools so we don't get C7 in a 10pull
|
||||
pools.removeFromAllPools(new int[] {itemId});
|
||||
}
|
||||
addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
|
||||
int constItemId = itemId + 100;
|
||||
GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
|
||||
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
|
||||
inventory.addItem(constItemId, 1);
|
||||
}
|
||||
isTransferItem = true;
|
||||
break;
|
||||
}
|
||||
// Const check
|
||||
int constellation = checkPlayerAvatarConstellationLevel(player, itemId);
|
||||
switch (constellation) {
|
||||
case -2: // Is weapon
|
||||
switch (itemData.getRankLevel()) {
|
||||
case 5 -> addStarglitter = 10;
|
||||
case 4 -> addStarglitter = 2;
|
||||
default -> addStardust = 15;
|
||||
}
|
||||
break;
|
||||
case -1: // New character
|
||||
gachaItem.setIsGachaItemNew(true);
|
||||
break;
|
||||
default:
|
||||
if (constellation >= 6) { // C6, give consolation starglitter
|
||||
addStarglitter = (itemData.getRankLevel()==5)? 25 : 5;
|
||||
} else { // C0-C5, give constellation item
|
||||
if (banner.isRemoveC6FromPool() && constellation == 5) { // New C6, remove it from the pools so we don't get C7 in a 10pull
|
||||
pools.removeFromAllPools(new int[] {itemId});
|
||||
}
|
||||
addStarglitter = (itemData.getRankLevel()==5)? 10 : 2;
|
||||
int constItemId = itemId + 100;
|
||||
GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId);
|
||||
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null));
|
||||
inventory.addItem(constItemId, 1);
|
||||
}
|
||||
isTransferItem = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create item
|
||||
GameItem item = new GameItem(itemData);
|
||||
gachaItem.setGachaItem(item.toItemParam());
|
||||
inventory.addItem(item);
|
||||
// Create item
|
||||
GameItem item = new GameItem(itemData);
|
||||
gachaItem.setGachaItem(item.toItemParam());
|
||||
inventory.addItem(item);
|
||||
|
||||
stardust += addStardust;
|
||||
starglitter += addStarglitter;
|
||||
stardust += addStardust;
|
||||
starglitter += addStarglitter;
|
||||
|
||||
if (addStardust > 0) {
|
||||
gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust));
|
||||
}
|
||||
if (addStarglitter > 0) {
|
||||
ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build();
|
||||
if (isTransferItem) {
|
||||
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam));
|
||||
}
|
||||
gachaItem.addTokenItemList(starglitterParam);
|
||||
}
|
||||
if (addStardust > 0) {
|
||||
gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust));
|
||||
}
|
||||
if (addStarglitter > 0) {
|
||||
ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build();
|
||||
if (isTransferItem) {
|
||||
gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam));
|
||||
}
|
||||
gachaItem.addTokenItemList(starglitterParam);
|
||||
}
|
||||
|
||||
list.add(gachaItem.build());
|
||||
}
|
||||
list.add(gachaItem.build());
|
||||
}
|
||||
|
||||
// Add stardust/starglitter
|
||||
if (stardust > 0) {
|
||||
inventory.addItem(stardustId, stardust);
|
||||
}
|
||||
if (starglitter > 0) {
|
||||
inventory.addItem(starglitterId, starglitter);
|
||||
}
|
||||
// Add stardust/starglitter
|
||||
if (stardust > 0) {
|
||||
inventory.addItem(stardustId, stardust);
|
||||
}
|
||||
if (starglitter > 0) {
|
||||
inventory.addItem(starglitterId, starglitter);
|
||||
}
|
||||
|
||||
// Packets
|
||||
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo));
|
||||
// Packets
|
||||
player.sendPacket(new PacketDoGachaRsp(banner, list, gachaInfo));
|
||||
|
||||
// Battle Pass trigger
|
||||
player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_GACHA_NUM, 0, times);
|
||||
}
|
||||
// Battle Pass trigger
|
||||
player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_GACHA_NUM, 0, times);
|
||||
}
|
||||
|
||||
private synchronized void startWatcher(GameServer server) {
|
||||
if(this.watchService == null) {
|
||||
try {
|
||||
this.watchService = FileSystems.getDefault().newWatchService();
|
||||
Path path = new File(DATA()).toPath();
|
||||
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Cannot reinitialise watcher ");
|
||||
}
|
||||
}
|
||||
private synchronized void startWatcher(GameServer server) {
|
||||
if (this.watchService == null) {
|
||||
try {
|
||||
this.watchService = FileSystems.getDefault().newWatchService();
|
||||
Path path = new File(DATA()).toPath();
|
||||
path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload");
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Cannot reinitialise watcher ");
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public synchronized void watchBannerJson(GameServerTickEvent tickEvent) {
|
||||
if(GAME_OPTIONS.watchGachaConfig) {
|
||||
try {
|
||||
WatchKey watchKey = watchService.take();
|
||||
@Subscribe
|
||||
public synchronized void watchBannerJson(GameServerTickEvent tickEvent) {
|
||||
if (GAME_OPTIONS.watchGachaConfig) {
|
||||
try {
|
||||
WatchKey watchKey = watchService.take();
|
||||
|
||||
for (WatchEvent<?> event : watchKey.pollEvents()) {
|
||||
final Path changed = (Path) event.context();
|
||||
if (changed.endsWith("Banners.json")) {
|
||||
Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config");
|
||||
this.load();
|
||||
}
|
||||
}
|
||||
for (WatchEvent<?> event : watchKey.pollEvents()) {
|
||||
final Path changed = (Path) event.context();
|
||||
if (changed.endsWith("Banners.json")) {
|
||||
Grasscutter.getLogger().info("Change detected with banners.json. Reloading gacha config");
|
||||
this.load();
|
||||
}
|
||||
}
|
||||
|
||||
boolean valid = watchKey.reset();
|
||||
if (!valid) {
|
||||
Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work.");
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean valid = watchKey.reset();
|
||||
if (!valid) {
|
||||
Grasscutter.getLogger().error("Unable to reset Gacha Manager Watch Key. Auto-reload of banners.json will no longer work.");
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized GetGachaInfoRsp createProto(Player player) {
|
||||
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
|
||||
private synchronized GetGachaInfoRsp createProto(Player player) {
|
||||
GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345);
|
||||
|
||||
long currentTime = System.currentTimeMillis() / 1000L;
|
||||
long currentTime = System.currentTimeMillis() / 1000L;
|
||||
|
||||
for (GachaBanner banner : getGachaBanners().values()) {
|
||||
if ((banner.getEndTime() >= currentTime && banner.getBeginTime() <= currentTime) || (banner.getBannerType() == BannerType.STANDARD))
|
||||
{
|
||||
proto.addGachaInfoList(banner.toProto(player));
|
||||
}
|
||||
}
|
||||
for (GachaBanner banner : getGachaBanners().values()) {
|
||||
if ((banner.getEndTime() >= currentTime && banner.getBeginTime() <= currentTime) || (banner.getBannerType() == BannerType.STANDARD))
|
||||
{
|
||||
proto.addGachaInfoList(banner.toProto(player));
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
return proto.build();
|
||||
}
|
||||
|
||||
public GetGachaInfoRsp toProto(Player player) {
|
||||
return createProto(player);
|
||||
}
|
||||
public GetGachaInfoRsp toProto(Player player) {
|
||||
return createProto(player);
|
||||
}
|
||||
}
|
||||
|
@ -33,199 +33,199 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
||||
public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
private final Long2ObjectMap<GameItem> store;
|
||||
private final Int2ObjectMap<InventoryTab> inventoryTypes;
|
||||
|
||||
public Inventory(Player player) {
|
||||
super(player);
|
||||
|
||||
this.store = new Long2ObjectOpenHashMap<>();
|
||||
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(INVENTORY_LIMITS.weapons));
|
||||
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(INVENTORY_LIMITS.relics));
|
||||
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(INVENTORY_LIMITS.materials));
|
||||
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(INVENTORY_LIMITS.furniture));
|
||||
}
|
||||
private final Long2ObjectMap<GameItem> store;
|
||||
private final Int2ObjectMap<InventoryTab> inventoryTypes;
|
||||
|
||||
public AvatarStorage getAvatarStorage() {
|
||||
return this.getPlayer().getAvatars();
|
||||
}
|
||||
public Inventory(Player player) {
|
||||
super(player);
|
||||
|
||||
public Long2ObjectMap<GameItem> getItems() {
|
||||
return store;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
|
||||
return inventoryTypes;
|
||||
}
|
||||
|
||||
public InventoryTab getInventoryTab(ItemType type) {
|
||||
return getInventoryTypes().get(type.getValue());
|
||||
}
|
||||
|
||||
public void createInventoryTab(ItemType type, InventoryTab tab) {
|
||||
this.getInventoryTypes().put(type.getValue(), tab);
|
||||
}
|
||||
|
||||
public GameItem getItemByGuid(long id) {
|
||||
return this.getItems().get(id);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId) {
|
||||
return addItem(itemId, 1);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId, int count) {
|
||||
this.store = new Long2ObjectOpenHashMap<>();
|
||||
this.inventoryTypes = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(INVENTORY_LIMITS.weapons));
|
||||
this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(INVENTORY_LIMITS.relics));
|
||||
this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(INVENTORY_LIMITS.materials));
|
||||
this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(INVENTORY_LIMITS.furniture));
|
||||
}
|
||||
|
||||
public AvatarStorage getAvatarStorage() {
|
||||
return this.getPlayer().getAvatars();
|
||||
}
|
||||
|
||||
public Long2ObjectMap<GameItem> getItems() {
|
||||
return store;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<InventoryTab> getInventoryTypes() {
|
||||
return inventoryTypes;
|
||||
}
|
||||
|
||||
public InventoryTab getInventoryTab(ItemType type) {
|
||||
return getInventoryTypes().get(type.getValue());
|
||||
}
|
||||
|
||||
public void createInventoryTab(ItemType type, InventoryTab tab) {
|
||||
this.getInventoryTypes().put(type.getValue(), tab);
|
||||
}
|
||||
|
||||
public GameItem getItemByGuid(long id) {
|
||||
return this.getItems().get(id);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId) {
|
||||
return addItem(itemId, 1);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId, int count) {
|
||||
return addItem(itemId, count, null);
|
||||
}
|
||||
|
||||
public boolean addItem(int itemId, int count, ActionReason reason) {
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
|
||||
if (itemData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GameItem item = new GameItem(itemData, count);
|
||||
|
||||
return addItem(item, reason);
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item) {
|
||||
GameItem result = putItem(item);
|
||||
|
||||
if (result != null) {
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item, ActionReason reason) {
|
||||
boolean result = addItem(item);
|
||||
|
||||
if (result && reason != null) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
|
||||
boolean result = addItem(item);
|
||||
public boolean addItem(int itemId, int count, ActionReason reason) {
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
|
||||
if (reason != null && (forceNotify || result)) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
||||
}
|
||||
if (itemData == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean addItem(ItemParamData itemParam) {
|
||||
GameItem item = new GameItem(itemData, count);
|
||||
|
||||
return addItem(item, reason);
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item) {
|
||||
GameItem result = putItem(item);
|
||||
|
||||
if (result != null) {
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item, ActionReason reason) {
|
||||
boolean result = addItem(item);
|
||||
|
||||
if (result && reason != null) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean addItem(GameItem item, ActionReason reason, boolean forceNotify) {
|
||||
boolean result = addItem(item);
|
||||
|
||||
if (reason != null && (forceNotify || result)) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(item, reason));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean addItem(ItemParamData itemParam) {
|
||||
return addItem(itemParam, null);
|
||||
}
|
||||
|
||||
public boolean addItem(ItemParamData itemParam, ActionReason reason) {
|
||||
if (itemParam == null) return false;
|
||||
|
||||
public boolean addItem(ItemParamData itemParam, ActionReason reason) {
|
||||
if (itemParam == null) return false;
|
||||
return addItem(itemParam.getId(), itemParam.getCount(), reason);
|
||||
}
|
||||
|
||||
public void addItems(Collection<GameItem> items) {
|
||||
this.addItems(items, null);
|
||||
}
|
||||
|
||||
public void addItems(Collection<GameItem> items, ActionReason reason) {
|
||||
List<GameItem> changedItems = new LinkedList<>();
|
||||
|
||||
for (GameItem item : items) {
|
||||
GameItem result = putItem(item);
|
||||
|
||||
if (result != null) {
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
|
||||
changedItems.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedItems.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason != null) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
}
|
||||
|
||||
public void addItemParams(Collection<ItemParam> items) {
|
||||
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null);
|
||||
}
|
||||
|
||||
public void addItemParamDatas(Collection<ItemParamData> items) {
|
||||
addItemParamDatas(items, null);
|
||||
}
|
||||
|
||||
public void addItemParamDatas(Collection<ItemParamData> items, ActionReason reason) {
|
||||
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason);
|
||||
}
|
||||
|
||||
private synchronized GameItem putItem(GameItem item) {
|
||||
// Dont add items that dont have a valid item definition.
|
||||
if (item.getItemData() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add item to inventory store
|
||||
ItemType type = item.getItemData().getItemType();
|
||||
InventoryTab tab = getInventoryTab(type);
|
||||
|
||||
// Add
|
||||
switch (type) {
|
||||
case ITEM_WEAPON:
|
||||
case ITEM_RELIQUARY:
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
// Duplicates cause problems
|
||||
item.setCount(Math.max(item.getCount(), 1));
|
||||
// Adds to inventory
|
||||
|
||||
public void addItems(Collection<GameItem> items) {
|
||||
this.addItems(items, null);
|
||||
}
|
||||
|
||||
public void addItems(Collection<GameItem> items, ActionReason reason) {
|
||||
List<GameItem> changedItems = new LinkedList<>();
|
||||
|
||||
for (GameItem item : items) {
|
||||
GameItem result = putItem(item);
|
||||
|
||||
if (result != null) {
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_OBTAIN_MATERIAL_NUM, result.getItemId(), result.getCount());
|
||||
changedItems.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (changedItems.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason != null) {
|
||||
getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
}
|
||||
|
||||
public void addItemParams(Collection<ItemParam> items) {
|
||||
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null);
|
||||
}
|
||||
|
||||
public void addItemParamDatas(Collection<ItemParamData> items) {
|
||||
addItemParamDatas(items, null);
|
||||
}
|
||||
|
||||
public void addItemParamDatas(Collection<ItemParamData> items, ActionReason reason) {
|
||||
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason);
|
||||
}
|
||||
|
||||
private synchronized GameItem putItem(GameItem item) {
|
||||
// Dont add items that dont have a valid item definition.
|
||||
if (item.getItemData() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add item to inventory store
|
||||
ItemType type = item.getItemData().getItemType();
|
||||
InventoryTab tab = getInventoryTab(type);
|
||||
|
||||
// Add
|
||||
switch (type) {
|
||||
case ITEM_WEAPON:
|
||||
case ITEM_RELIQUARY:
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
// Duplicates cause problems
|
||||
item.setCount(Math.max(item.getCount(), 1));
|
||||
// Adds to inventory
|
||||
this.putItem(item, tab);
|
||||
// Set ownership and save to db
|
||||
item.save();
|
||||
return item;
|
||||
case ITEM_VIRTUAL:
|
||||
// Handle
|
||||
this.addVirtualItem(item.getItemId(), item.getCount());
|
||||
return item;
|
||||
// Set ownership and save to db
|
||||
item.save();
|
||||
return item;
|
||||
case ITEM_VIRTUAL:
|
||||
// Handle
|
||||
this.addVirtualItem(item.getItemId(), item.getCount());
|
||||
return item;
|
||||
default:
|
||||
switch (item.getItemData().getMaterialType()) {
|
||||
case MATERIAL_ADSORBATE:
|
||||
this.player.getEnergyManager().handlePickupElemBall(item);
|
||||
return null;
|
||||
case MATERIAL_AVATAR:
|
||||
// Get avatar id
|
||||
int avatarId = (item.getItemId() % 1000) + 10000000;
|
||||
// Dont let people give themselves extra main characters
|
||||
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
return null;
|
||||
}
|
||||
// Add avatar
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
|
||||
this.player.addAvatar(new Avatar(avatarData));
|
||||
}
|
||||
return null;
|
||||
case MATERIAL_FLYCLOAK:
|
||||
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
|
||||
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
|
||||
this.player.addFlycloak(item.getItemId());
|
||||
}
|
||||
return null;
|
||||
case MATERIAL_COSTUME:
|
||||
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
|
||||
this.player.getEnergyManager().handlePickupElemBall(item);
|
||||
return null;
|
||||
case MATERIAL_AVATAR:
|
||||
// Get avatar id
|
||||
int avatarId = (item.getItemId() % 1000) + 10000000;
|
||||
// Dont let people give themselves extra main characters
|
||||
if (avatarId == GameConstants.MAIN_CHARACTER_MALE || avatarId == GameConstants.MAIN_CHARACTER_FEMALE) {
|
||||
return null;
|
||||
}
|
||||
// Add avatar
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData != null && !this.player.getAvatars().hasAvatar(avatarId)) {
|
||||
this.player.addAvatar(new Avatar(avatarData));
|
||||
}
|
||||
return null;
|
||||
case MATERIAL_FLYCLOAK:
|
||||
AvatarFlycloakData flycloakData = GameData.getAvatarFlycloakDataMap().get(item.getItemId());
|
||||
if (flycloakData != null && !this.player.getFlyCloakList().contains(item.getItemId())) {
|
||||
this.player.addFlycloak(item.getItemId());
|
||||
}
|
||||
return null;
|
||||
case MATERIAL_COSTUME:
|
||||
AvatarCostumeData costumeData = GameData.getAvatarCostumeDataItemIdMap().get(item.getItemId());
|
||||
if (costumeData != null && !this.player.getCostumeList().contains(costumeData.getId())) {
|
||||
this.player.addCostume(costumeData.getId());
|
||||
}
|
||||
@ -233,46 +233,46 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
case MATERIAL_NAMECARD:
|
||||
if (!this.player.getNameCardList().contains(item.getItemId())) {
|
||||
this.player.addNameCard(item.getItemId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
if (tab == null) {
|
||||
return null;
|
||||
}
|
||||
GameItem existingItem = tab.getItemById(item.getItemId());
|
||||
if (existingItem == null) {
|
||||
// Item type didnt exist before, we will add it to main inventory map if there is enough space
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
this.putItem(item, tab);
|
||||
// Set ownership and save to db
|
||||
item.save();
|
||||
return item;
|
||||
} else {
|
||||
// Add count
|
||||
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
|
||||
existingItem.save();
|
||||
return existingItem;
|
||||
}
|
||||
}
|
||||
GameItem existingItem = tab.getItemById(item.getItemId());
|
||||
if (existingItem == null) {
|
||||
// Item type didnt exist before, we will add it to main inventory map if there is enough space
|
||||
if (tab.getSize() >= tab.getMaxCapacity()) {
|
||||
return null;
|
||||
}
|
||||
this.putItem(item, tab);
|
||||
// Set ownership and save to db
|
||||
item.save();
|
||||
return item;
|
||||
} else {
|
||||
// Add count
|
||||
existingItem.setCount(Math.min(existingItem.getCount() + item.getCount(), item.getItemData().getStackLimit()));
|
||||
existingItem.save();
|
||||
return existingItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void putItem(GameItem item, InventoryTab tab) {
|
||||
}
|
||||
|
||||
private synchronized void putItem(GameItem item, InventoryTab tab) {
|
||||
this.player.getCodex().checkAddedItem(item);
|
||||
// Set owner and guid FIRST!
|
||||
item.setOwner(this.player);
|
||||
// Put in item store
|
||||
getItems().put(item.getGuid(), item);
|
||||
if (tab != null) {
|
||||
tab.onAddItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void addVirtualItem(int itemId, int count) {
|
||||
switch (itemId) {
|
||||
case 101 -> // Character exp
|
||||
// Put in item store
|
||||
getItems().put(item.getGuid(), item);
|
||||
if (tab != null) {
|
||||
tab.onAddItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void addVirtualItem(int itemId, int count) {
|
||||
switch (itemId) {
|
||||
case 101 -> // Character exp
|
||||
this.player.getServer().getInventorySystem().upgradeAvatar(this.player, this.player.getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
|
||||
case 102 -> // Adventure exp
|
||||
this.player.addExpDirectly(count);
|
||||
@ -290,216 +290,216 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
this.player.setCrystals(this.player.getCrystals() + count);
|
||||
case 204 -> // Home Coin
|
||||
this.player.setHomeCoin(this.player.getHomeCoin() + count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getVirtualItemCount(int itemId) {
|
||||
switch (itemId) {
|
||||
case 201: // Primogem
|
||||
return this.player.getPrimogems();
|
||||
case 202: // Mora
|
||||
return this.player.getMora();
|
||||
case 203: // Genesis Crystals
|
||||
return this.player.getCrystals();
|
||||
case 106: // Resin
|
||||
return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
private int getVirtualItemCount(int itemId) {
|
||||
switch (itemId) {
|
||||
case 201: // Primogem
|
||||
return this.player.getPrimogems();
|
||||
case 202: // Mora
|
||||
return this.player.getMora();
|
||||
case 203: // Genesis Crystals
|
||||
return this.player.getCrystals();
|
||||
case 106: // Resin
|
||||
return this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
case 107: // Legendary Key
|
||||
return this.player.getProperty(PlayerProperty.PROP_PLAYER_LEGENDARY_KEY);
|
||||
case 204: // Home Coin
|
||||
return this.player.getHomeCoin();
|
||||
default:
|
||||
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
|
||||
return (item == null) ? 0 : item.getCount();
|
||||
}
|
||||
}
|
||||
case 204: // Home Coin
|
||||
return this.player.getHomeCoin();
|
||||
default:
|
||||
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
|
||||
return (item == null) ? 0 : item.getCount();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean payItem(int id, int count) {
|
||||
return payItem(new ItemParamData(id, count));
|
||||
}
|
||||
public boolean payItem(int id, int count) {
|
||||
return payItem(new ItemParamData(id, count));
|
||||
}
|
||||
|
||||
public boolean payItem(ItemParamData costItem) {
|
||||
return payItems(new ItemParamData[] {costItem}, 1, null);
|
||||
}
|
||||
public boolean payItem(ItemParamData costItem) {
|
||||
return payItems(new ItemParamData[] {costItem}, 1, null);
|
||||
}
|
||||
|
||||
public boolean payItems(ItemParamData[] costItems) {
|
||||
return payItems(costItems, 1, null);
|
||||
}
|
||||
public boolean payItems(ItemParamData[] costItems) {
|
||||
return payItems(costItems, 1, null);
|
||||
}
|
||||
|
||||
public boolean payItems(ItemParamData[] costItems, int quantity) {
|
||||
return payItems(costItems, quantity, null);
|
||||
}
|
||||
|
||||
public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) {
|
||||
// Make sure player has requisite items
|
||||
for (ItemParamData cost : costItems) {
|
||||
if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// All costs are satisfied, now remove them all
|
||||
for (ItemParamData cost : costItems) {
|
||||
switch (cost.getId()) {
|
||||
case 201 -> // Primogem
|
||||
player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity));
|
||||
case 202 -> // Mora
|
||||
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
||||
case 203 -> // Genesis Crystals
|
||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||
case 106 -> // Resin
|
||||
player.getResinManager().useResin(cost.getCount() * quantity);
|
||||
public boolean payItems(ItemParamData[] costItems, int quantity) {
|
||||
return payItems(costItems, quantity, null);
|
||||
}
|
||||
|
||||
public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) {
|
||||
// Make sure player has requisite items
|
||||
for (ItemParamData cost : costItems) {
|
||||
if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// All costs are satisfied, now remove them all
|
||||
for (ItemParamData cost : costItems) {
|
||||
switch (cost.getId()) {
|
||||
case 201 -> // Primogem
|
||||
player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity));
|
||||
case 202 -> // Mora
|
||||
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
||||
case 203 -> // Genesis Crystals
|
||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||
case 106 -> // Resin
|
||||
player.getResinManager().useResin(cost.getCount() * quantity);
|
||||
case 107 -> // LegendaryKey
|
||||
player.useLegendaryKey(cost.getCount() * quantity);
|
||||
case 204 -> // Home Coin
|
||||
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
|
||||
default ->
|
||||
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
||||
}
|
||||
}
|
||||
case 204 -> // Home Coin
|
||||
player.setHomeCoin(player.getHomeCoin() - (cost.getCount() * quantity));
|
||||
default ->
|
||||
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
||||
}
|
||||
}
|
||||
|
||||
if (reason != null) { // Do we need these?
|
||||
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
||||
}
|
||||
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeItems(List<GameItem> items) {
|
||||
// TODO Bulk delete
|
||||
for (GameItem item : items) {
|
||||
this.removeItem(item, item.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeItem(long guid) {
|
||||
return removeItem(guid, 1);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(long guid, int count) {
|
||||
GameItem item = this.getItemByGuid(guid);
|
||||
|
||||
if (item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeItem(item, count);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GameItem item) {
|
||||
return removeItem(item, item.getCount());
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GameItem item, int count) {
|
||||
// Sanity check
|
||||
if (count <= 0 || item == null) {
|
||||
return false;
|
||||
}
|
||||
if (reason != null) { // Do we need these?
|
||||
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
|
||||
}
|
||||
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemData().isEquip()) {
|
||||
item.setCount(0);
|
||||
} else {
|
||||
item.setCount(item.getCount() - count);
|
||||
}
|
||||
|
||||
if (item.getCount() <= 0) {
|
||||
// Remove from inventory tab too
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
// Remove if less than 0
|
||||
deleteItem(item, tab);
|
||||
//
|
||||
getPlayer().sendPacket(new PacketStoreItemDelNotify(item));
|
||||
} else {
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
|
||||
}
|
||||
|
||||
// Battle pass trigger
|
||||
int removeCount = Math.min(count, item.getCount());
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
|
||||
|
||||
// Update in db
|
||||
item.save();
|
||||
|
||||
// Returns true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteItem(GameItem item, InventoryTab tab) {
|
||||
getItems().remove(item.getGuid());
|
||||
if (tab != null) {
|
||||
tab.onRemoveItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equipItem(long avatarGuid, long equipGuid) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
GameItem item = this.getItemByGuid(equipGuid);
|
||||
|
||||
if (avatar != null && item != null) {
|
||||
return avatar.equipItem(item, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean unequipItem(long avatarGuid, int slot) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
EquipType equipType = EquipType.getTypeByValue(slot);
|
||||
|
||||
if (avatar != null && equipType != EquipType.EQUIP_WEAPON) {
|
||||
if (avatar.unequipItem(equipType)) {
|
||||
getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(avatar, equipType));
|
||||
avatar.recalcStats();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<GameItem> items = DatabaseHelper.getInventoryItems(getPlayer());
|
||||
|
||||
for (GameItem item : items) {
|
||||
// Should never happen
|
||||
if (item.getObjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(item.getItemId());
|
||||
if (itemData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.setItemData(itemData);
|
||||
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
|
||||
putItem(item, tab);
|
||||
|
||||
// Equip to a character if possible
|
||||
if (item.isEquipped()) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter());
|
||||
boolean hasEquipped = false;
|
||||
|
||||
if (avatar != null) {
|
||||
hasEquipped = avatar.equipItem(item, false);
|
||||
}
|
||||
|
||||
if (!hasEquipped) {
|
||||
item.setEquipCharacter(0);
|
||||
item.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public void removeItems(List<GameItem> items) {
|
||||
// TODO Bulk delete
|
||||
for (GameItem item : items) {
|
||||
this.removeItem(item, item.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<GameItem> iterator() {
|
||||
return this.getItems().values().iterator();
|
||||
}
|
||||
public boolean removeItem(long guid) {
|
||||
return removeItem(guid, 1);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(long guid, int count) {
|
||||
GameItem item = this.getItemByGuid(guid);
|
||||
|
||||
if (item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeItem(item, count);
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GameItem item) {
|
||||
return removeItem(item, item.getCount());
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GameItem item, int count) {
|
||||
// Sanity check
|
||||
if (count <= 0 || item == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.getItemData().isEquip()) {
|
||||
item.setCount(0);
|
||||
} else {
|
||||
item.setCount(item.getCount() - count);
|
||||
}
|
||||
|
||||
if (item.getCount() <= 0) {
|
||||
// Remove from inventory tab too
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
// Remove if less than 0
|
||||
deleteItem(item, tab);
|
||||
//
|
||||
getPlayer().sendPacket(new PacketStoreItemDelNotify(item));
|
||||
} else {
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(item));
|
||||
}
|
||||
|
||||
// Battle pass trigger
|
||||
int removeCount = Math.min(count, item.getCount());
|
||||
getPlayer().getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, item.getItemId(), removeCount);
|
||||
|
||||
// Update in db
|
||||
item.save();
|
||||
|
||||
// Returns true on success
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deleteItem(GameItem item, InventoryTab tab) {
|
||||
getItems().remove(item.getGuid());
|
||||
if (tab != null) {
|
||||
tab.onRemoveItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equipItem(long avatarGuid, long equipGuid) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
GameItem item = this.getItemByGuid(equipGuid);
|
||||
|
||||
if (avatar != null && item != null) {
|
||||
return avatar.equipItem(item, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean unequipItem(long avatarGuid, int slot) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(avatarGuid);
|
||||
EquipType equipType = EquipType.getTypeByValue(slot);
|
||||
|
||||
if (avatar != null && equipType != EquipType.EQUIP_WEAPON) {
|
||||
if (avatar.unequipItem(equipType)) {
|
||||
getPlayer().sendPacket(new PacketAvatarEquipChangeNotify(avatar, equipType));
|
||||
avatar.recalcStats();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<GameItem> items = DatabaseHelper.getInventoryItems(getPlayer());
|
||||
|
||||
for (GameItem item : items) {
|
||||
// Should never happen
|
||||
if (item.getObjectId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(item.getItemId());
|
||||
if (itemData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
item.setItemData(itemData);
|
||||
|
||||
InventoryTab tab = null;
|
||||
if (item.getItemData() != null) {
|
||||
tab = getInventoryTab(item.getItemData().getItemType());
|
||||
}
|
||||
|
||||
putItem(item, tab);
|
||||
|
||||
// Equip to a character if possible
|
||||
if (item.isEquipped()) {
|
||||
Avatar avatar = getPlayer().getAvatars().getAvatarById(item.getEquipCharacter());
|
||||
boolean hasEquipped = false;
|
||||
|
||||
if (avatar != null) {
|
||||
hasEquipped = avatar.equipItem(item, false);
|
||||
}
|
||||
|
||||
if (!hasEquipped) {
|
||||
item.setEquipCharacter(0);
|
||||
item.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<GameItem> iterator() {
|
||||
return this.getItems().values().iterator();
|
||||
}
|
||||
}
|
||||
|
@ -13,89 +13,89 @@ import emu.grasscutter.server.packet.send.PacketDelMailRsp;
|
||||
import emu.grasscutter.server.packet.send.PacketMailChangeNotify;
|
||||
|
||||
public class MailHandler extends BasePlayerManager {
|
||||
private final List<Mail> mail;
|
||||
|
||||
public MailHandler(Player player) {
|
||||
super(player);
|
||||
|
||||
this.mail = new ArrayList<>();
|
||||
}
|
||||
private final List<Mail> mail;
|
||||
|
||||
public List<Mail> getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
// ---------------------MAIL------------------------
|
||||
public MailHandler(Player player) {
|
||||
super(player);
|
||||
|
||||
public void sendMail(Mail message) {
|
||||
// Call mail receive event.
|
||||
PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this.getPlayer(), message); event.call();
|
||||
if(event.isCanceled()) return; message = event.getMessage();
|
||||
|
||||
message.setOwnerUid(this.getPlayer().getUid());
|
||||
message.save();
|
||||
|
||||
this.mail.add(message);
|
||||
|
||||
Grasscutter.getLogger().debug("Mail sent to user [" + this.getPlayer().getUid() + ":" + this.getPlayer().getNickname() + "]!");
|
||||
|
||||
if (this.getPlayer().isOnline()) {
|
||||
this.getPlayer().sendPacket(new PacketMailChangeNotify(this.getPlayer(), message));
|
||||
} // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline
|
||||
}
|
||||
this.mail = new ArrayList<>();
|
||||
}
|
||||
|
||||
public boolean deleteMail(int mailId) {
|
||||
Mail message = getMailById(mailId);
|
||||
public List<Mail> getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
this.getMail().remove(mailId);
|
||||
message.expireTime = 0;
|
||||
message.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
// ---------------------MAIL------------------------
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void deleteMail(List<Integer> mailList) {
|
||||
List<Integer> sortedMailList = new ArrayList<>();
|
||||
sortedMailList.addAll(mailList);
|
||||
Collections.sort(sortedMailList, Collections.reverseOrder());
|
||||
|
||||
List<Integer> deleted = new ArrayList<>();
|
||||
|
||||
for (int id : sortedMailList) {
|
||||
if (this.deleteMail(id)) {
|
||||
deleted.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
player.getSession().send(new PacketDelMailRsp(player, deleted));
|
||||
player.getSession().send(new PacketMailChangeNotify(player, null, deleted));
|
||||
}
|
||||
public void sendMail(Mail message) {
|
||||
// Call mail receive event.
|
||||
PlayerReceiveMailEvent event = new PlayerReceiveMailEvent(this.getPlayer(), message); event.call();
|
||||
if (event.isCanceled()) return; message = event.getMessage();
|
||||
|
||||
public Mail getMailById(int index) { return this.mail.get(index); }
|
||||
|
||||
public int getMailIndex(Mail message) {
|
||||
return this.mail.indexOf(message);
|
||||
}
|
||||
message.setOwnerUid(this.getPlayer().getUid());
|
||||
message.save();
|
||||
|
||||
public boolean replaceMailByIndex(int index, Mail message) {
|
||||
if(getMailById(index) != null) {
|
||||
this.mail.set(index, message);
|
||||
message.save();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this.mail.add(message);
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<Mail> mailList = DatabaseHelper.getAllMail(this.getPlayer());
|
||||
|
||||
for (Mail mail : mailList) {
|
||||
this.getMail().add(mail);
|
||||
}
|
||||
}
|
||||
Grasscutter.getLogger().debug("Mail sent to user [" + this.getPlayer().getUid() + ":" + this.getPlayer().getNickname() + "]!");
|
||||
|
||||
if (this.getPlayer().isOnline()) {
|
||||
this.getPlayer().sendPacket(new PacketMailChangeNotify(this.getPlayer(), message));
|
||||
} // TODO: setup a way for the mail notification to show up when someone receives mail when they were offline
|
||||
}
|
||||
|
||||
public boolean deleteMail(int mailId) {
|
||||
Mail message = getMailById(mailId);
|
||||
|
||||
if (message != null) {
|
||||
this.getMail().remove(mailId);
|
||||
message.expireTime = 0;
|
||||
message.save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void deleteMail(List<Integer> mailList) {
|
||||
List<Integer> sortedMailList = new ArrayList<>();
|
||||
sortedMailList.addAll(mailList);
|
||||
Collections.sort(sortedMailList, Collections.reverseOrder());
|
||||
|
||||
List<Integer> deleted = new ArrayList<>();
|
||||
|
||||
for (int id : sortedMailList) {
|
||||
if (this.deleteMail(id)) {
|
||||
deleted.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
player.getSession().send(new PacketDelMailRsp(player, deleted));
|
||||
player.getSession().send(new PacketMailChangeNotify(player, null, deleted));
|
||||
}
|
||||
|
||||
public Mail getMailById(int index) { return this.mail.get(index); }
|
||||
|
||||
public int getMailIndex(Mail message) {
|
||||
return this.mail.indexOf(message);
|
||||
}
|
||||
|
||||
public boolean replaceMailByIndex(int index, Mail message) {
|
||||
if (getMailById(index) != null) {
|
||||
this.mail.set(index, message);
|
||||
message.save();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<Mail> mailList = DatabaseHelper.getAllMail(this.getPlayer());
|
||||
|
||||
for (Mail mail : mailList) {
|
||||
this.getMail().add(mail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,9 +102,9 @@ public class CookingManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
// Get result item information.
|
||||
int qualityIndex =
|
||||
quality == 0
|
||||
? 2
|
||||
int qualityIndex =
|
||||
quality == 0
|
||||
? 2
|
||||
: quality - 1;
|
||||
|
||||
ItemParamData resultParam = recipeData.getQualityOutputVec().get(qualityIndex);
|
||||
|
@ -15,25 +15,25 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FurnitureManager extends BasePlayerManager {
|
||||
|
||||
|
||||
public FurnitureManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public void onLogin(){
|
||||
public void onLogin() {
|
||||
notifyUnlockFurniture();
|
||||
notifyUnlockFurnitureSuite();
|
||||
}
|
||||
|
||||
public void notifyUnlockFurniture(){
|
||||
public void notifyUnlockFurniture() {
|
||||
player.getSession().send(new PacketUnlockedFurnitureFormulaDataNotify(player.getUnlockedFurniture()));
|
||||
}
|
||||
|
||||
public void notifyUnlockFurnitureSuite(){
|
||||
public void notifyUnlockFurnitureSuite() {
|
||||
player.getSession().send(new PacketUnlockedFurnitureSuiteDataNotify(player.getUnlockedFurnitureSuite()));
|
||||
}
|
||||
|
||||
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem){
|
||||
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem) {
|
||||
// Check
|
||||
if (!List.of("ITEM_USE_UNLOCK_FURNITURE_FORMULA", "ITEM_USE_UNLOCK_FURNITURE_SUITE")
|
||||
.contains(useItem.getItemData().getItemUse().get(0).getUseOp())) {
|
||||
@ -45,10 +45,10 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
// Remove first
|
||||
player.getInventory().removeItem(useItem, 1);
|
||||
|
||||
if("ITEM_USE_UNLOCK_FURNITURE_FORMULA".equals(useItem.getItemData().getItemUse().get(0).getUseOp())){
|
||||
if ("ITEM_USE_UNLOCK_FURNITURE_FORMULA".equals(useItem.getItemData().getItemUse().get(0).getUseOp())) {
|
||||
player.getUnlockedFurniture().add(furnitureIdOrSuiteId);
|
||||
notifyUnlockFurniture();
|
||||
}else{
|
||||
}else {
|
||||
player.getUnlockedFurnitureSuite().add(furnitureIdOrSuiteId);
|
||||
notifyUnlockFurnitureSuite();
|
||||
}
|
||||
@ -57,19 +57,19 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
|
||||
public void startMake(int makeId, int avatarId) {
|
||||
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
|
||||
if(makeData == null){
|
||||
if (makeData == null) {
|
||||
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, null));
|
||||
return;
|
||||
}
|
||||
|
||||
// check slot count
|
||||
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()){
|
||||
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()) {
|
||||
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_SLOT_FULL_VALUE, null));
|
||||
return;
|
||||
}
|
||||
|
||||
// pay items first
|
||||
if(!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))){
|
||||
if (!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))) {
|
||||
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_HOME_FURNITURE_COUNT_NOT_ENOUGH_VALUE, null));
|
||||
return;
|
||||
}
|
||||
@ -93,7 +93,7 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
public void queryStatus() {
|
||||
if (player.getHome().getFurnitureMakeSlotItemList() == null){
|
||||
if (player.getHome().getFurnitureMakeSlotItemList() == null) {
|
||||
player.getHome().setFurnitureMakeSlotItemList(new ArrayList<>());
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
|
||||
public void take(int index, int makeId, boolean isFastFinish) {
|
||||
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
|
||||
if(makeData == null){
|
||||
if (makeData == null) {
|
||||
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, makeId, null, null));
|
||||
return;
|
||||
}
|
||||
@ -112,19 +112,19 @@ public class FurnitureManager extends BasePlayerManager {
|
||||
.filter(x -> x.getIndex() == index && x.getMakeId() == makeId)
|
||||
.findFirst();
|
||||
|
||||
if(slotItem.isEmpty()){
|
||||
if (slotItem.isEmpty()) {
|
||||
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_NO_MAKE_DATA_VALUE, makeId, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
// pay the speedup item
|
||||
if(isFastFinish && !player.getInventory().payItem(107013,1)){
|
||||
if (isFastFinish && !player.getInventory().payItem(107013,1)) {
|
||||
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
|
||||
return;
|
||||
}
|
||||
|
||||
// check if player can take
|
||||
// if(slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish){
|
||||
// if (slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish) {
|
||||
// player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
|
||||
// return;
|
||||
// }
|
||||
|
@ -26,7 +26,7 @@ public class ResinManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
|
||||
|
||||
// Check if the player has sufficient resin.
|
||||
if (currentResin < amount) {
|
||||
return false;
|
||||
@ -39,16 +39,16 @@ public class ResinManager extends BasePlayerManager {
|
||||
// Check if this has taken the player under the recharge cap,
|
||||
// starting the recharging process.
|
||||
if (this.player.getNextResinRefresh() == 0 && newResin < GAME_OPTIONS.resinOptions.cap) {
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
|
||||
}
|
||||
|
||||
// Send packets.
|
||||
this.player.sendPacket(new PacketResinChangeNotify(this.player));
|
||||
|
||||
|
||||
// Battle Pass trigger
|
||||
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, 106, amount); // Resin item id = 106
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ public class ResinManager extends BasePlayerManager {
|
||||
* Player login.
|
||||
********************/
|
||||
public synchronized void onPlayerLogin() {
|
||||
// If resin usage is disabled, set resin to cap.
|
||||
// If resin usage is disabled, set resin to cap.
|
||||
if (!GAME_OPTIONS.resinOptions.resinUsage) {
|
||||
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, GAME_OPTIONS.resinOptions.cap);
|
||||
this.player.setNextResinRefresh(0);
|
||||
@ -132,7 +132,7 @@ public class ResinManager extends BasePlayerManager {
|
||||
// we need to restart recharging here.
|
||||
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
|
||||
|
||||
if (currentResin < GAME_OPTIONS.resinOptions.cap && this.player.getNextResinRefresh() == 0) {
|
||||
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
|
||||
}
|
||||
|
@ -1,191 +1,191 @@
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
// Statue of the Seven Manager
|
||||
public class SotSManager extends BasePlayerManager {
|
||||
|
||||
// NOTE: Spring volume balance *1 = fight prop HP *100
|
||||
|
||||
private final Logger logger = Grasscutter.getLogger();
|
||||
private Timer autoRecoverTimer;
|
||||
private final boolean enablePriorityHealing = false;
|
||||
|
||||
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
|
||||
|
||||
public SotSManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean getIsAutoRecoveryEnabled() {
|
||||
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
|
||||
}
|
||||
|
||||
public void setIsAutoRecoveryEnabled(boolean enabled) {
|
||||
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getAutoRecoveryPercentage() {
|
||||
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
|
||||
}
|
||||
|
||||
public void setAutoRecoveryPercentage(int percentage) {
|
||||
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public long getLastUsed() {
|
||||
return player.getSpringLastUsed();
|
||||
}
|
||||
|
||||
public void setLastUsed() {
|
||||
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getMaxVolume() {
|
||||
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
|
||||
}
|
||||
|
||||
public void setMaxVolume(int volume) {
|
||||
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getCurrentVolume() {
|
||||
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
|
||||
}
|
||||
|
||||
public void setCurrentVolume(int volume) {
|
||||
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
|
||||
setLastUsed();
|
||||
player.save();
|
||||
}
|
||||
|
||||
public void handleEnterTransPointRegionNotify() {
|
||||
logger.trace("Player entered statue region");
|
||||
autoRevive();
|
||||
if (autoRecoverTimer == null) {
|
||||
autoRecoverTimer = new Timer();
|
||||
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleExitTransPointRegionNotify() {
|
||||
logger.trace("Player left statue region");
|
||||
if (autoRecoverTimer != null) {
|
||||
autoRecoverTimer.cancel();
|
||||
autoRecoverTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// autoRevive automatically revives all team members.
|
||||
public void autoRevive() {
|
||||
player.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||
boolean isAlive = entity.isAlive();
|
||||
if (isAlive) {
|
||||
return;
|
||||
}
|
||||
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
|
||||
player.getTeamManager().reviveAvatar(entity.getAvatar());
|
||||
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
|
||||
});
|
||||
}
|
||||
|
||||
private class AutoRecoverTimerTick extends TimerTask {
|
||||
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
|
||||
public void run() {
|
||||
refillSpringVolume();
|
||||
|
||||
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
|
||||
|
||||
if (getIsAutoRecoveryEnabled()) {
|
||||
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
|
||||
// When the statue does not have enough remaining volume:
|
||||
// Enhanced experience: Enable priority healing
|
||||
// The current active character will get healed first, then sequential.
|
||||
// Vanilla experience: Disable priority healing
|
||||
// Sequential healing based on character index.
|
||||
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
|
||||
if (priorityIndex >= 0) {
|
||||
checkAndHealAvatar(activeTeam.get(priorityIndex));
|
||||
}
|
||||
for (int i = 0; i < activeTeam.size(); i++) {
|
||||
if (i != priorityIndex) {
|
||||
checkAndHealAvatar(activeTeam.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAndHealAvatar(EntityAvatar entity) {
|
||||
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
|
||||
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
|
||||
if (currentHP == maxHP) {
|
||||
return;
|
||||
}
|
||||
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
|
||||
|
||||
if (targetHP > currentHP) {
|
||||
int needHP = targetHP - currentHP;
|
||||
int currentVolume = getCurrentVolume();
|
||||
if (currentVolume >= needHP) {
|
||||
// sufficient
|
||||
setCurrentVolume(currentVolume - needHP);
|
||||
} else {
|
||||
// insufficient balance
|
||||
needHP = currentVolume;
|
||||
setCurrentVolume(0);
|
||||
}
|
||||
if (needHP > 0) {
|
||||
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
|
||||
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
|
||||
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
|
||||
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
|
||||
ChangeHpReason.CHANGE_HP_REASON_ADD_STATUE));
|
||||
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refillSpringVolume() {
|
||||
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
|
||||
// TODO: remove
|
||||
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
|
||||
setMaxVolume(8500000);
|
||||
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
|
||||
// TODO: remove
|
||||
setAutoRecoveryPercentage(100);
|
||||
setIsAutoRecoveryEnabled(true);
|
||||
|
||||
int maxVolume = getMaxVolume();
|
||||
int currentVolume = getCurrentVolume();
|
||||
if (currentVolume < maxVolume) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
int secondsSinceLastUsed = (int) (now - getLastUsed());
|
||||
// 15s = 1% max volume
|
||||
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
|
||||
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
|
||||
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
|
||||
logger.trace("Statue remaining HP volume: " + currentVolume);
|
||||
setCurrentVolume(currentVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.managers;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
// Statue of the Seven Manager
|
||||
public class SotSManager extends BasePlayerManager {
|
||||
|
||||
// NOTE: Spring volume balance *1 = fight prop HP *100
|
||||
|
||||
private final Logger logger = Grasscutter.getLogger();
|
||||
private Timer autoRecoverTimer;
|
||||
private final boolean enablePriorityHealing = false;
|
||||
|
||||
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
|
||||
|
||||
public SotSManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public boolean getIsAutoRecoveryEnabled() {
|
||||
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
|
||||
}
|
||||
|
||||
public void setIsAutoRecoveryEnabled(boolean enabled) {
|
||||
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getAutoRecoveryPercentage() {
|
||||
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
|
||||
}
|
||||
|
||||
public void setAutoRecoveryPercentage(int percentage) {
|
||||
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public long getLastUsed() {
|
||||
return player.getSpringLastUsed();
|
||||
}
|
||||
|
||||
public void setLastUsed() {
|
||||
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getMaxVolume() {
|
||||
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
|
||||
}
|
||||
|
||||
public void setMaxVolume(int volume) {
|
||||
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
|
||||
player.save();
|
||||
}
|
||||
|
||||
public int getCurrentVolume() {
|
||||
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
|
||||
}
|
||||
|
||||
public void setCurrentVolume(int volume) {
|
||||
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
|
||||
setLastUsed();
|
||||
player.save();
|
||||
}
|
||||
|
||||
public void handleEnterTransPointRegionNotify() {
|
||||
logger.trace("Player entered statue region");
|
||||
autoRevive();
|
||||
if (autoRecoverTimer == null) {
|
||||
autoRecoverTimer = new Timer();
|
||||
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleExitTransPointRegionNotify() {
|
||||
logger.trace("Player left statue region");
|
||||
if (autoRecoverTimer != null) {
|
||||
autoRecoverTimer.cancel();
|
||||
autoRecoverTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// autoRevive automatically revives all team members.
|
||||
public void autoRevive() {
|
||||
player.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||
boolean isAlive = entity.isAlive();
|
||||
if (isAlive) {
|
||||
return;
|
||||
}
|
||||
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
|
||||
player.getTeamManager().reviveAvatar(entity.getAvatar());
|
||||
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
|
||||
});
|
||||
}
|
||||
|
||||
private class AutoRecoverTimerTick extends TimerTask {
|
||||
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
|
||||
public void run() {
|
||||
refillSpringVolume();
|
||||
|
||||
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
|
||||
|
||||
if (getIsAutoRecoveryEnabled()) {
|
||||
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
|
||||
// When the statue does not have enough remaining volume:
|
||||
// Enhanced experience: Enable priority healing
|
||||
// The current active character will get healed first, then sequential.
|
||||
// Vanilla experience: Disable priority healing
|
||||
// Sequential healing based on character index.
|
||||
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
|
||||
if (priorityIndex >= 0) {
|
||||
checkAndHealAvatar(activeTeam.get(priorityIndex));
|
||||
}
|
||||
for (int i = 0; i < activeTeam.size(); i++) {
|
||||
if (i != priorityIndex) {
|
||||
checkAndHealAvatar(activeTeam.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAndHealAvatar(EntityAvatar entity) {
|
||||
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
|
||||
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
|
||||
if (currentHP == maxHP) {
|
||||
return;
|
||||
}
|
||||
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
|
||||
|
||||
if (targetHP > currentHP) {
|
||||
int needHP = targetHP - currentHP;
|
||||
int currentVolume = getCurrentVolume();
|
||||
if (currentVolume >= needHP) {
|
||||
// sufficient
|
||||
setCurrentVolume(currentVolume - needHP);
|
||||
} else {
|
||||
// insufficient balance
|
||||
needHP = currentVolume;
|
||||
setCurrentVolume(0);
|
||||
}
|
||||
if (needHP > 0) {
|
||||
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
|
||||
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
|
||||
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
|
||||
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
|
||||
ChangeHpReason.CHANGE_HP_REASON_ADD_STATUE));
|
||||
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void refillSpringVolume() {
|
||||
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
|
||||
// TODO: remove
|
||||
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
|
||||
setMaxVolume(8500000);
|
||||
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
|
||||
// TODO: remove
|
||||
setAutoRecoveryPercentage(100);
|
||||
setIsAutoRecoveryEnabled(true);
|
||||
|
||||
int maxVolume = getMaxVolume();
|
||||
int currentVolume = getCurrentVolume();
|
||||
if (currentVolume < maxVolume) {
|
||||
long now = System.currentTimeMillis() / 1000;
|
||||
int secondsSinceLastUsed = (int) (now - getLastUsed());
|
||||
// 15s = 1% max volume
|
||||
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
|
||||
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
|
||||
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
|
||||
logger.trace("Statue remaining HP volume: " + currentVolume);
|
||||
setCurrentVolume(currentVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,197 +21,197 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChatManager implements ChatManagerHandler {
|
||||
static final String PREFIXES = "[/!]";
|
||||
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
|
||||
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
|
||||
static final String PREFIXES = "[/!]";
|
||||
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
|
||||
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
|
||||
|
||||
// We store the chat history for ongoing sessions in the form
|
||||
// user id -> chat partner id -> [messages]
|
||||
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
|
||||
// We store the chat history for ongoing sessions in the form
|
||||
// user id -> chat partner id -> [messages]
|
||||
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
|
||||
|
||||
private final GameServer server;
|
||||
private final GameServer server;
|
||||
|
||||
public ChatManager(GameServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
public ChatManager(GameServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
|
||||
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches())
|
||||
return false;
|
||||
for (String line : rawMessage.substring(1).split("\n[/!]"))
|
||||
CommandMap.getInstance().invoke(sender, target, line);
|
||||
return true;
|
||||
}
|
||||
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
|
||||
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches())
|
||||
return false;
|
||||
for (String line : rawMessage.substring(1).split("\n[/!]"))
|
||||
CommandMap.getInstance().invoke(sender, target, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************
|
||||
* Chat history handling
|
||||
********************/
|
||||
private void putInHistory(int uid, int targetId, ChatInfo info) {
|
||||
if (!this.history.containsKey(uid)) {
|
||||
this.history.put(uid, new HashMap<>());
|
||||
}
|
||||
if (!this.history.get(uid).containsKey(targetId)) {
|
||||
this.history.get(uid).put(targetId, new ArrayList<>());
|
||||
}
|
||||
/********************
|
||||
* Chat history handling
|
||||
********************/
|
||||
private void putInHistory(int uid, int targetId, ChatInfo info) {
|
||||
if (!this.history.containsKey(uid)) {
|
||||
this.history.put(uid, new HashMap<>());
|
||||
}
|
||||
if (!this.history.get(uid).containsKey(targetId)) {
|
||||
this.history.get(uid).put(targetId, new ArrayList<>());
|
||||
}
|
||||
|
||||
this.history.get(uid).get(targetId).add(info);
|
||||
}
|
||||
this.history.get(uid).get(targetId).add(info);
|
||||
}
|
||||
|
||||
public void clearHistoryOnLogout(Player player) {
|
||||
if (this.history.containsKey(player.getUid())) {
|
||||
this.history.remove(player.getUid());
|
||||
}
|
||||
}
|
||||
public void clearHistoryOnLogout(Player player) {
|
||||
if (this.history.containsKey(player.getUid())) {
|
||||
this.history.remove(player.getUid());
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePullPrivateChatReq(Player player, int targetUid) {
|
||||
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) {
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid)));
|
||||
}
|
||||
else {
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(List.of()));
|
||||
}
|
||||
}
|
||||
public void handlePullPrivateChatReq(Player player, int targetUid) {
|
||||
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) {
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid)));
|
||||
}
|
||||
else {
|
||||
player.sendPacket(new PacketPullPrivateChatRsp(List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePullRecentChatReq(Player player) {
|
||||
// For now, we send the list three messages from the server for the recent chat history.
|
||||
// This matches the previous behavior, but ultimately, we should probably keep track of the last chat partner
|
||||
// for every given player and return the last messages exchanged with that partner.
|
||||
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) {
|
||||
int historyLength = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
|
||||
var messages = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).subList(Math.max(historyLength - 3, 0), historyLength);
|
||||
player.sendPacket(new PacketPullRecentChatRsp(messages));
|
||||
}
|
||||
else {
|
||||
player.sendPacket(new PacketPullRecentChatRsp(List.of()));
|
||||
}
|
||||
}
|
||||
public void handlePullRecentChatReq(Player player) {
|
||||
// For now, we send the list three messages from the server for the recent chat history.
|
||||
// This matches the previous behavior, but ultimately, we should probably keep track of the last chat partner
|
||||
// for every given player and return the last messages exchanged with that partner.
|
||||
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) {
|
||||
int historyLength = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
|
||||
var messages = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).subList(Math.max(historyLength - 3, 0), historyLength);
|
||||
player.sendPacket(new PacketPullRecentChatRsp(messages));
|
||||
}
|
||||
else {
|
||||
player.sendPacket(new PacketPullRecentChatRsp(List.of()));
|
||||
}
|
||||
}
|
||||
|
||||
/********************
|
||||
* Sending messages
|
||||
********************/
|
||||
public void sendPrivateMessageFromServer(int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
/********************
|
||||
* Sending messages
|
||||
********************/
|
||||
public void sendPrivateMessageFromServer(int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
public void sendPrivateMessageFromServer(int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
public void sendPrivateMessageFromServer(int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
// Create chat packet and put in history.
|
||||
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
|
||||
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
// Send.
|
||||
target.sendPacket(packet);
|
||||
}
|
||||
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, target, message)) {
|
||||
return;
|
||||
}
|
||||
public void sendPrivateMessage(Player player, int targetUid, String message) {
|
||||
// Sanity checks.
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, target, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send and put in history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
|
||||
|
||||
public void sendPrivateMessage(Player player, int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
|
||||
// Send and put in history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
// Send and put is history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
public void sendPrivateMessage(Player player, int targetUid, int emote) {
|
||||
// Get target.
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
|
||||
public void sendTeamMessage(Player player, int channel, String message) {
|
||||
// Sanity checks
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, null, message)) {
|
||||
return;
|
||||
}
|
||||
// Create chat packet.
|
||||
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
|
||||
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
|
||||
}
|
||||
public void sendTeamMessage(Player player, int channel, int icon) {
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
|
||||
}
|
||||
// Send and put is history.
|
||||
player.sendPacket(packet);
|
||||
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
|
||||
|
||||
/********************
|
||||
* Welcome messages
|
||||
********************/
|
||||
public void sendServerWelcomeMessages(Player player) {
|
||||
var joinOptions = GAME_INFO.joinOptions;
|
||||
|
||||
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
|
||||
}
|
||||
|
||||
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
|
||||
}
|
||||
if (target != null) {
|
||||
target.sendPacket(packet);
|
||||
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
|
||||
}
|
||||
}
|
||||
|
||||
this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3");
|
||||
}
|
||||
public void sendTeamMessage(Player player, int channel, String message) {
|
||||
// Sanity checks
|
||||
if (message == null || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if command
|
||||
if (tryInvokeCommand(player, null, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
|
||||
}
|
||||
public void sendTeamMessage(Player player, int channel, int icon) {
|
||||
// Create and send chat packet
|
||||
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
|
||||
}
|
||||
|
||||
/********************
|
||||
* Welcome messages
|
||||
********************/
|
||||
public void sendServerWelcomeMessages(Player player) {
|
||||
var joinOptions = GAME_INFO.joinOptions;
|
||||
|
||||
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
|
||||
}
|
||||
|
||||
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
|
||||
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
|
||||
}
|
||||
|
||||
this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3");
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class DeforestationManager extends BasePlayerManager {
|
||||
|
||||
private final ArrayList<HitTreeRecord> currentRecord;
|
||||
private final static HashMap<Integer, Integer> ColliderTypeToWoodItemID = new HashMap<>();
|
||||
|
||||
|
||||
static {
|
||||
/* define wood types which reflected to item id*/
|
||||
ColliderTypeToWoodItemID.put(1,101301);
|
||||
@ -37,17 +37,17 @@ public class DeforestationManager extends BasePlayerManager {
|
||||
ColliderTypeToWoodItemID.put(11,101311);
|
||||
ColliderTypeToWoodItemID.put(12,101312);
|
||||
}
|
||||
|
||||
public DeforestationManager(Player player){
|
||||
|
||||
public DeforestationManager(Player player) {
|
||||
super(player);
|
||||
this.currentRecord = new ArrayList<>();
|
||||
}
|
||||
public void resetWood(){
|
||||
public void resetWood() {
|
||||
synchronized (currentRecord) {
|
||||
currentRecord.clear();
|
||||
}
|
||||
}
|
||||
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit){
|
||||
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit) {
|
||||
synchronized (currentRecord) {
|
||||
//Grasscutter.getLogger().info("onDeforestationInvoke! Wood records {}", currentRecord);
|
||||
VectorOuterClass.Vector hitPosition = hit.getTreePos();
|
||||
@ -59,14 +59,14 @@ public class DeforestationManager extends BasePlayerManager {
|
||||
HitTreeRecord record = searchRecord(positionHash);
|
||||
if (record == null) {
|
||||
record = new HitTreeRecord(positionHash);
|
||||
}else{
|
||||
}else {
|
||||
currentRecord.remove(record);// move it to last position
|
||||
}
|
||||
currentRecord.add(record);
|
||||
if(currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE){
|
||||
if (currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE) {
|
||||
currentRecord.remove(0);
|
||||
}
|
||||
if(record.record()) {
|
||||
if (record.record()) {
|
||||
EntityItem entity = new EntityItem(scene,
|
||||
null,
|
||||
GameData.getItemDataMap().get(itemId),
|
||||
@ -82,7 +82,7 @@ public class DeforestationManager extends BasePlayerManager {
|
||||
}
|
||||
// unknown wood type
|
||||
}
|
||||
private HitTreeRecord searchRecord(int id){
|
||||
private HitTreeRecord searchRecord(int id) {
|
||||
for (HitTreeRecord record : currentRecord) {
|
||||
if (record.getUnique() == id) {
|
||||
return record;
|
||||
|
@ -448,4 +448,4 @@ public class EnergyManager extends BasePlayerManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,298 +30,298 @@ import emu.grasscutter.utils.Utils;
|
||||
|
||||
public class ForgingManager extends BasePlayerManager {
|
||||
|
||||
public ForgingManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
public ForgingManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
/**********
|
||||
Blueprint unlocking.
|
||||
**********/
|
||||
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
|
||||
// Make sure this is actually a forging blueprint.
|
||||
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
|
||||
return false;
|
||||
}
|
||||
/**********
|
||||
Blueprint unlocking.
|
||||
**********/
|
||||
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
|
||||
// Make sure this is actually a forging blueprint.
|
||||
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine the forging item we should unlock.
|
||||
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
|
||||
// Determine the forging item we should unlock.
|
||||
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
|
||||
|
||||
// Remove the blueprint from the player's inventory.
|
||||
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
|
||||
// update when unlocking the blueprint.
|
||||
player.getInventory().removeItem(blueprintItem, 1);
|
||||
// Remove the blueprint from the player's inventory.
|
||||
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
|
||||
// update when unlocking the blueprint.
|
||||
player.getInventory().removeItem(blueprintItem, 1);
|
||||
|
||||
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
|
||||
this.player.getUnlockedForgingBlueprints().add(forgeId);
|
||||
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
|
||||
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
|
||||
this.player.getUnlockedForgingBlueprints().add(forgeId);
|
||||
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**********
|
||||
Communicate forging information to the client.
|
||||
**********/
|
||||
private synchronized int determineNumberOfQueues() {
|
||||
int adventureRank = player.getLevel();
|
||||
return
|
||||
(adventureRank >= 15) ? 4
|
||||
: (adventureRank >= 10) ? 3
|
||||
: (adventureRank >= 5) ? 2
|
||||
: 1;
|
||||
}
|
||||
/**********
|
||||
Communicate forging information to the client.
|
||||
**********/
|
||||
private synchronized int determineNumberOfQueues() {
|
||||
int adventureRank = player.getLevel();
|
||||
return
|
||||
(adventureRank >= 15) ? 4
|
||||
: (adventureRank >= 10) ? 3
|
||||
: (adventureRank >= 5) ? 2
|
||||
: 1;
|
||||
}
|
||||
|
||||
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
|
||||
Map<Integer, ForgeQueueData> res = new HashMap<>();
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
|
||||
Map<Integer, ForgeQueueData> res = new HashMap<>();
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
|
||||
// Create queue information for all active forges.
|
||||
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
|
||||
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
|
||||
// Create queue information for all active forges.
|
||||
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
|
||||
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
|
||||
|
||||
ForgeQueueData data = ForgeQueueData.newBuilder()
|
||||
.setQueueId(i + 1)
|
||||
.setForgeId(activeForge.getForgeId())
|
||||
.setFinishCount(activeForge.getFinishedCount(currentTime))
|
||||
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
|
||||
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
|
||||
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
|
||||
.setAvatarId(activeForge.getAvatarId())
|
||||
.build();
|
||||
ForgeQueueData data = ForgeQueueData.newBuilder()
|
||||
.setQueueId(i + 1)
|
||||
.setForgeId(activeForge.getForgeId())
|
||||
.setFinishCount(activeForge.getFinishedCount(currentTime))
|
||||
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
|
||||
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
|
||||
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
|
||||
.setAvatarId(activeForge.getAvatarId())
|
||||
.build();
|
||||
|
||||
res.put(i + 1, data);
|
||||
}
|
||||
res.put(i + 1, data);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public synchronized void sendForgeDataNotify() {
|
||||
// Determine the number of queues and unlocked items.
|
||||
int numQueues = this.determineNumberOfQueues();
|
||||
var unlockedItems = this.player.getUnlockedForgingBlueprints();
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
public synchronized void sendForgeDataNotify() {
|
||||
// Determine the number of queues and unlocked items.
|
||||
int numQueues = this.determineNumberOfQueues();
|
||||
var unlockedItems = this.player.getUnlockedForgingBlueprints();
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
|
||||
// Send notification.
|
||||
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
|
||||
}
|
||||
|
||||
public synchronized void handleForgeGetQueueDataReq() {
|
||||
// Determine the number of queues.
|
||||
int numQueues = this.determineNumberOfQueues();
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
// Send notification.
|
||||
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
|
||||
}
|
||||
|
||||
// Reply.
|
||||
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
|
||||
}
|
||||
public synchronized void handleForgeGetQueueDataReq() {
|
||||
// Determine the number of queues.
|
||||
int numQueues = this.determineNumberOfQueues();
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
|
||||
/**********
|
||||
Initiate forging process.
|
||||
**********/
|
||||
private synchronized void sendForgeQueueDataNotify() {
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
|
||||
}
|
||||
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
// Reply.
|
||||
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
|
||||
}
|
||||
|
||||
if (hasRemoved) {
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
|
||||
}
|
||||
/**********
|
||||
Initiate forging process.
|
||||
**********/
|
||||
private synchronized void sendForgeQueueDataNotify() {
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
|
||||
}
|
||||
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
|
||||
var queueData = this.determineCurrentForgeQueueData();
|
||||
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
|
||||
}
|
||||
if (hasRemoved) {
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
|
||||
}
|
||||
|
||||
public synchronized void handleForgeStartReq(ForgeStartReq req) {
|
||||
// Refuse if all queues are already full.
|
||||
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
|
||||
return;
|
||||
}
|
||||
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
|
||||
}
|
||||
|
||||
// Get the required forging information for the target item.
|
||||
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
|
||||
return;
|
||||
}
|
||||
public synchronized void handleForgeStartReq(ForgeStartReq req) {
|
||||
// Refuse if all queues are already full.
|
||||
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
|
||||
return;
|
||||
}
|
||||
|
||||
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
|
||||
// Get the required forging information for the target item.
|
||||
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if the player has sufficient forge points.
|
||||
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
|
||||
if (requiredPoints > this.player.getForgePoints()) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have enough of each material and consume.
|
||||
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, forgeData.getScoinCost()));
|
||||
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
|
||||
|
||||
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
|
||||
//Check if the player has sufficient forge points.
|
||||
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
|
||||
if (requiredPoints > this.player.getForgePoints()) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
|
||||
}
|
||||
// Check if we have enough of each material and consume.
|
||||
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, forgeData.getScoinCost()));
|
||||
|
||||
// Consume forge points.
|
||||
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
|
||||
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
|
||||
|
||||
// Create and add active forge.
|
||||
ActiveForgeData activeForge = new ActiveForgeData();
|
||||
activeForge.setForgeId(req.getForgeId());
|
||||
activeForge.setAvatarId(req.getAvatarId());
|
||||
activeForge.setCount(req.getForgeCount());
|
||||
activeForge.setStartTime(Utils.getCurrentSeconds());
|
||||
activeForge.setForgeTime(forgeData.getForgeTime());
|
||||
if (!success) {
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
|
||||
}
|
||||
|
||||
this.player.getActiveForges().add(activeForge);
|
||||
// Consume forge points.
|
||||
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
|
||||
|
||||
// Done.
|
||||
this.sendForgeQueueDataNotify();
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
|
||||
}
|
||||
// Create and add active forge.
|
||||
ActiveForgeData activeForge = new ActiveForgeData();
|
||||
activeForge.setForgeId(req.getForgeId());
|
||||
activeForge.setAvatarId(req.getAvatarId());
|
||||
activeForge.setCount(req.getForgeCount());
|
||||
activeForge.setStartTime(Utils.getCurrentSeconds());
|
||||
activeForge.setForgeTime(forgeData.getForgeTime());
|
||||
|
||||
/**********
|
||||
Forge queue manipulation (obtaining results and cancelling forges).
|
||||
**********/
|
||||
private synchronized void obtainItems(int queueId) {
|
||||
// Determin how many items are finished.
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
|
||||
this.player.getActiveForges().add(activeForge);
|
||||
|
||||
int finished = forge.getFinishedCount(currentTime);
|
||||
int unfinished = forge.getUnfinishedCount(currentTime);
|
||||
// Done.
|
||||
this.sendForgeQueueDataNotify();
|
||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
|
||||
}
|
||||
|
||||
// Sanity check: Are any items finished?
|
||||
if (finished <= 0) {
|
||||
return;
|
||||
}
|
||||
/**********
|
||||
Forge queue manipulation (obtaining results and cancelling forges).
|
||||
**********/
|
||||
private synchronized void obtainItems(int queueId) {
|
||||
// Determin how many items are finished.
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
|
||||
|
||||
// Give finished items to the player.
|
||||
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
|
||||
int finished = forge.getFinishedCount(currentTime);
|
||||
int unfinished = forge.getUnfinishedCount(currentTime);
|
||||
|
||||
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
|
||||
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
|
||||
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
|
||||
this.player.getInventory().addItem(addItem);
|
||||
|
||||
// Battle pass trigger handler
|
||||
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
|
||||
// Sanity check: Are any items finished?
|
||||
if (finished <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace active forge with a new one for the unfinished items, if there are any.
|
||||
if (unfinished > 0) {
|
||||
ActiveForgeData remainingForge = new ActiveForgeData();
|
||||
// Give finished items to the player.
|
||||
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
|
||||
|
||||
remainingForge.setForgeId(forge.getForgeId());
|
||||
remainingForge.setAvatarId(forge.getAvatarId());
|
||||
remainingForge.setCount(unfinished);
|
||||
remainingForge.setForgeTime(forge.getForgeTime());
|
||||
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
|
||||
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
|
||||
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
|
||||
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
|
||||
this.player.getInventory().addItem(addItem);
|
||||
|
||||
this.player.getActiveForges().set(queueId - 1, remainingForge);
|
||||
this.sendForgeQueueDataNotify();
|
||||
}
|
||||
// Otherwise, completely remove it.
|
||||
else {
|
||||
this.player.getActiveForges().remove(queueId - 1);
|
||||
// this.sendForgeQueueDataNotify(queueId);
|
||||
this.sendForgeQueueDataNotify(true);
|
||||
}
|
||||
// Battle pass trigger handler
|
||||
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
|
||||
|
||||
// Send response.
|
||||
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
|
||||
}
|
||||
// Replace active forge with a new one for the unfinished items, if there are any.
|
||||
if (unfinished > 0) {
|
||||
ActiveForgeData remainingForge = new ActiveForgeData();
|
||||
|
||||
private synchronized void cancelForge(int queueId) {
|
||||
// Make sure there are no unfinished items.
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
|
||||
remainingForge.setForgeId(forge.getForgeId());
|
||||
remainingForge.setAvatarId(forge.getAvatarId());
|
||||
remainingForge.setCount(unfinished);
|
||||
remainingForge.setForgeTime(forge.getForgeTime());
|
||||
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
|
||||
|
||||
if (forge.getFinishedCount(currentTime) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return material items to the player.
|
||||
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
|
||||
this.player.getActiveForges().set(queueId - 1, remainingForge);
|
||||
this.sendForgeQueueDataNotify();
|
||||
}
|
||||
// Otherwise, completely remove it.
|
||||
else {
|
||||
this.player.getActiveForges().remove(queueId - 1);
|
||||
// this.sendForgeQueueDataNotify(queueId);
|
||||
this.sendForgeQueueDataNotify(true);
|
||||
}
|
||||
|
||||
var returnItems = new ArrayList<GameItem>();
|
||||
for (var material : data.getMaterialItems()) {
|
||||
if (material.getItemId() == 0) {
|
||||
continue;
|
||||
}
|
||||
// Send response.
|
||||
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
|
||||
}
|
||||
|
||||
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
|
||||
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
|
||||
private synchronized void cancelForge(int queueId) {
|
||||
// Make sure there are no unfinished items.
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
|
||||
|
||||
this.player.getInventory().addItem(returnItem);
|
||||
returnItems.add(returnItem);
|
||||
}
|
||||
if (forge.getFinishedCount(currentTime) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return Mora to the player.
|
||||
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
|
||||
// Return material items to the player.
|
||||
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
|
||||
|
||||
ItemData moraItem = GameData.getItemDataMap().get(202);
|
||||
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
|
||||
returnItems.add(returnMora);
|
||||
var returnItems = new ArrayList<GameItem>();
|
||||
for (var material : data.getMaterialItems()) {
|
||||
if (material.getItemId() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return forge points to the player.
|
||||
int requiredPoints = data.getForgePoint() * forge.getCount();
|
||||
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
|
||||
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
|
||||
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
|
||||
|
||||
this.player.setForgePoints(newPoints);
|
||||
this.player.getInventory().addItem(returnItem);
|
||||
returnItems.add(returnItem);
|
||||
}
|
||||
|
||||
// Remove the forge queue.
|
||||
this.player.getActiveForges().remove(queueId - 1);
|
||||
this.sendForgeQueueDataNotify(true);
|
||||
// Return Mora to the player.
|
||||
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
|
||||
|
||||
// Send response.
|
||||
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
|
||||
}
|
||||
ItemData moraItem = GameData.getItemDataMap().get(202);
|
||||
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
|
||||
returnItems.add(returnMora);
|
||||
|
||||
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
|
||||
// Get info from the request.
|
||||
int queueId = req.getForgeQueueId();
|
||||
var manipulateType = req.getManipulateType();
|
||||
|
||||
// Handle according to the manipulation type.
|
||||
switch (manipulateType) {
|
||||
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
|
||||
this.obtainItems(queueId);
|
||||
break;
|
||||
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
|
||||
this.cancelForge(queueId);
|
||||
break;
|
||||
default:
|
||||
break; //Should never happen.
|
||||
}
|
||||
}
|
||||
// Return forge points to the player.
|
||||
int requiredPoints = data.getForgePoint() * forge.getCount();
|
||||
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
|
||||
|
||||
/**********
|
||||
Periodic forging updates.
|
||||
**********/
|
||||
public synchronized void sendPlayerForgingUpdate() {
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
this.player.setForgePoints(newPoints);
|
||||
|
||||
// Determine if sending an update is necessary.
|
||||
// We only send an update if there are forges in the forge queue
|
||||
// that have changed since the last notification.
|
||||
if (this.player.getActiveForges().size() <= 0) {
|
||||
return;
|
||||
}
|
||||
// Remove the forge queue.
|
||||
this.player.getActiveForges().remove(queueId - 1);
|
||||
this.sendForgeQueueDataNotify(true);
|
||||
|
||||
boolean hasChanges = this.player.getActiveForges().stream()
|
||||
.filter(forge -> forge.updateChanged(currentTime))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
// Send response.
|
||||
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
|
||||
}
|
||||
|
||||
if (!hasChanges) {
|
||||
return;
|
||||
}
|
||||
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
|
||||
// Get info from the request.
|
||||
int queueId = req.getForgeQueueId();
|
||||
var manipulateType = req.getManipulateType();
|
||||
|
||||
// Send notification.
|
||||
this.sendForgeQueueDataNotify();
|
||||
// Handle according to the manipulation type.
|
||||
switch (manipulateType) {
|
||||
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
|
||||
this.obtainItems(queueId);
|
||||
break;
|
||||
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
|
||||
this.cancelForge(queueId);
|
||||
break;
|
||||
default:
|
||||
break; //Should never happen.
|
||||
}
|
||||
}
|
||||
|
||||
// Reset changed flags.
|
||||
this.player.getActiveForges().stream()
|
||||
.forEach(forge -> forge.setChanged(false));
|
||||
}
|
||||
/**********
|
||||
Periodic forging updates.
|
||||
**********/
|
||||
public synchronized void sendPlayerForgingUpdate() {
|
||||
int currentTime = Utils.getCurrentSeconds();
|
||||
|
||||
// Determine if sending an update is necessary.
|
||||
// We only send an update if there are forges in the forge queue
|
||||
// that have changed since the last notification.
|
||||
if (this.player.getActiveForges().size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasChanges = this.player.getActiveForges().stream()
|
||||
.filter(forge -> forge.updateChanged(currentTime))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
|
||||
if (!hasChanges) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send notification.
|
||||
this.sendForgeQueueDataNotify();
|
||||
|
||||
// Reset changed flags.
|
||||
this.player.getActiveForges().stream()
|
||||
.forEach(forge -> forge.setChanged(false));
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ public class MapMarksManager extends BasePlayerManager {
|
||||
public MapMarksManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
|
||||
public Map<String, MapMark> getMapMarks() {
|
||||
return getPlayer().getMapMarks();
|
||||
}
|
||||
@ -51,7 +51,7 @@ public class MapMarksManager extends BasePlayerManager {
|
||||
}
|
||||
player.getSession().send(new PacketMarkMapRsp(getMapMarks()));
|
||||
}
|
||||
|
||||
|
||||
public String getMapMarkKey(Position position) {
|
||||
return "x" + (int)position.getX()+ "z" + (int)position.getZ();
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ public class StaminaManager extends BasePlayerManager {
|
||||
}};
|
||||
|
||||
public static void initialize() {
|
||||
// TODO: Initialize foods etc.
|
||||
// TODO: Initialize foods etc.
|
||||
}
|
||||
|
||||
public StaminaManager(Player player) {
|
||||
@ -529,20 +529,20 @@ public class StaminaManager extends BasePlayerManager {
|
||||
}
|
||||
// Bow avatar charged attack
|
||||
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
|
||||
|
||||
|
||||
switch (currentAvatar.getAvatarData().getWeaponType()) {
|
||||
case WEAPON_BOW:
|
||||
return getBowSustainedCost(skillCasting);
|
||||
case WEAPON_CLAYMORE:
|
||||
return getClaymoreSustainedCost(skillCasting);
|
||||
case WEAPON_CATALYST:
|
||||
return getCatalystCost(skillCasting);
|
||||
case WEAPON_POLE:
|
||||
return getPolearmCost(skillCasting);
|
||||
case WEAPON_SWORD_ONE_HAND:
|
||||
return getSwordCost(skillCasting);
|
||||
case WEAPON_BOW:
|
||||
return getBowSustainedCost(skillCasting);
|
||||
case WEAPON_CLAYMORE:
|
||||
return getClaymoreSustainedCost(skillCasting);
|
||||
case WEAPON_CATALYST:
|
||||
return getCatalystCost(skillCasting);
|
||||
case WEAPON_POLE:
|
||||
return getPolearmCost(skillCasting);
|
||||
case WEAPON_SWORD_ONE_HAND:
|
||||
return getSwordCost(skillCasting);
|
||||
}
|
||||
|
||||
|
||||
return new Consumption();
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,17 @@ import lombok.NonNull;
|
||||
|
||||
public abstract class BasePlayerDataManager {
|
||||
protected transient Player player;
|
||||
|
||||
|
||||
public BasePlayerDataManager() {}
|
||||
|
||||
|
||||
public BasePlayerDataManager(@NonNull Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
|
||||
public void setPlayer(Player player) {
|
||||
if (this.player == null) {
|
||||
this.player = player;
|
||||
|
@ -4,15 +4,15 @@ import lombok.NonNull;
|
||||
|
||||
public abstract class BasePlayerManager {
|
||||
protected transient final Player player;
|
||||
|
||||
|
||||
public BasePlayerManager(@NonNull Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves the player to the database
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,60 +8,60 @@ import dev.morphia.annotations.Entity;
|
||||
@Entity(useDiscriminator = false)
|
||||
public class PlayerCollectionRecords {
|
||||
private Map<Integer, CollectionRecord> records;
|
||||
|
||||
|
||||
private Map<Integer, CollectionRecord> getRecords() {
|
||||
if (records == null) {
|
||||
records = new HashMap<>();
|
||||
records = new HashMap<>();
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
public void addRecord(int configId, long expiredMillisecond){
|
||||
Map<Integer, CollectionRecord> records;
|
||||
|
||||
public void addRecord(int configId, long expiredMillisecond) {
|
||||
Map<Integer, CollectionRecord> records;
|
||||
synchronized (records = getRecords()) {
|
||||
records.put(configId, new CollectionRecord(configId, expiredMillisecond + System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean findRecord(int configId) {
|
||||
Map<Integer, CollectionRecord> records;
|
||||
Map<Integer, CollectionRecord> records;
|
||||
synchronized (records = getRecords()) {
|
||||
CollectionRecord record = records.get(configId);
|
||||
|
||||
if (record == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
|
||||
|
||||
if (expired) {
|
||||
records.remove(configId);
|
||||
return false;
|
||||
CollectionRecord record = records.get(configId);
|
||||
|
||||
if (record == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
|
||||
|
||||
if (expired) {
|
||||
records.remove(configId);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Entity
|
||||
public static class CollectionRecord {
|
||||
private int configId;
|
||||
private long expiredTime;
|
||||
|
||||
@Deprecated // Morphia
|
||||
public CollectionRecord() {}
|
||||
|
||||
private int configId;
|
||||
private long expiredTime;
|
||||
|
||||
@Deprecated // Morphia
|
||||
public CollectionRecord() {}
|
||||
|
||||
public CollectionRecord(int configId, long expiredTime) {
|
||||
this.configId = configId;
|
||||
this.expiredTime = expiredTime;
|
||||
}
|
||||
|
||||
public int getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
public int getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
|
||||
public long getExpiredTime() {
|
||||
return expiredTime;
|
||||
}
|
||||
public long getExpiredTime() {
|
||||
return expiredTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ import static emu.grasscutter.game.props.OpenState.*;
|
||||
public class PlayerOpenStateManager extends BasePlayerDataManager {
|
||||
// Map of all open states that this player has. Do not put default values here.
|
||||
private Map<Integer, Integer> map;
|
||||
|
||||
|
||||
/*
|
||||
//DO NOT MODIFY. Based on conversation of official server and client, game version 2.7
|
||||
private static Set<OpenState> newPlayerOpenStates = Set.of(OPEN_STATE_DERIVATIVE_MALL,OPEN_STATE_PHOTOGRAPH,OPEN_STATE_BATTLE_PASS,OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL,OPEN_STATE_SHOP_TYPE_RECOMMANDED,
|
||||
@ -27,7 +27,7 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
|
||||
OPEN_STATE_WEAPON_PROMOTE,OPEN_STATE_AVATAR_PROMOTE,OPEN_STATE_AVATAR_TALENT,OPEN_STATE_WEAPON_UPGRADE,OPEN_STATE_RESIN,OPEN_STATE_RELIQUARY_UPGRADE,
|
||||
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP,OPEN_STATE_RELIQUARY_PROMOTE);
|
||||
*/
|
||||
|
||||
|
||||
// For development. Remove entry when properly implemented
|
||||
// TODO - Set as boolean in OpenState
|
||||
public static final Set<OpenState> DEV_OPEN_STATES = Stream.of(OpenState.values())
|
||||
@ -38,25 +38,25 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
|
||||
super(player);
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getOpenStateMap() {
|
||||
if (this.map == null) this.map = new HashMap<>();
|
||||
return this.map;
|
||||
}
|
||||
public Map<Integer, Integer> getOpenStateMap() {
|
||||
if (this.map == null) this.map = new HashMap<>();
|
||||
return this.map;
|
||||
}
|
||||
|
||||
public int getOpenState(OpenState openState) {
|
||||
return this.map.getOrDefault(openState.getValue(), 0);
|
||||
}
|
||||
|
||||
|
||||
public void setOpenState(OpenState openState, Integer value) {
|
||||
Integer previousValue = this.map.getOrDefault(openState.getValue(),0);
|
||||
if(value != previousValue) {
|
||||
if (value != previousValue) {
|
||||
this.map.put(openState.getValue(), value);
|
||||
player.getSession().send(new PacketOpenStateChangeNotify(openState.getValue(),value));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOpenStates(Map<OpenState,Integer> openStatesChanged) {
|
||||
for(Map.Entry<OpenState, Integer> entry : openStatesChanged.entrySet()) {
|
||||
for (Map.Entry<OpenState, Integer> entry : openStatesChanged.entrySet()) {
|
||||
setOpenState(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
@ -70,4 +70,4 @@ public class PlayerOpenStateManager extends BasePlayerDataManager {
|
||||
.filter(s -> s.getUnlockLevel() > 1 && s.getUnlockLevel() <= this.player.getLevel())
|
||||
.forEach(s -> this.setOpenState(s, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,75 +10,75 @@ import emu.grasscutter.game.avatar.Avatar;
|
||||
|
||||
@Entity
|
||||
public class TeamInfo {
|
||||
private String name;
|
||||
private List<Integer> avatars;
|
||||
|
||||
public TeamInfo() {
|
||||
this.name = "";
|
||||
this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
}
|
||||
private String name;
|
||||
private List<Integer> avatars;
|
||||
|
||||
public TeamInfo(List<Integer> avatars) {
|
||||
this.name = "";
|
||||
this.avatars = avatars;
|
||||
}
|
||||
public TeamInfo() {
|
||||
this.name = "";
|
||||
this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public TeamInfo(List<Integer> avatars) {
|
||||
this.name = "";
|
||||
this.avatars = avatars;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Integer> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return avatars.size();
|
||||
}
|
||||
|
||||
public boolean contains(Avatar avatar) {
|
||||
return getAvatars().contains(avatar.getAvatarId());
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean addAvatar(Avatar avatar) {
|
||||
if (contains(avatar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getAvatars().add(avatar.getAvatarId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeAvatar(int slot) {
|
||||
if (size() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getAvatars().remove(slot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void copyFrom(TeamInfo team) {
|
||||
copyFrom(team, GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
}
|
||||
|
||||
public void copyFrom(TeamInfo team, int maxTeamSize) {
|
||||
// Clone avatar ids from team to copy from
|
||||
List<Integer> avatarIds = new ArrayList<>(team.getAvatars());
|
||||
|
||||
// Clear current avatar list first
|
||||
this.getAvatars().clear();
|
||||
|
||||
// Copy from team
|
||||
int len = Math.min(avatarIds.size(), maxTeamSize);
|
||||
for (int i = 0; i < len; i++) {
|
||||
int id = avatarIds.get(i);
|
||||
this.getAvatars().add(id);
|
||||
}
|
||||
}
|
||||
public List<Integer> getAvatars() {
|
||||
return avatars;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return avatars.size();
|
||||
}
|
||||
|
||||
public boolean contains(Avatar avatar) {
|
||||
return getAvatars().contains(avatar.getAvatarId());
|
||||
}
|
||||
|
||||
public boolean addAvatar(Avatar avatar) {
|
||||
if (contains(avatar)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getAvatars().add(avatar.getAvatarId());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeAvatar(int slot) {
|
||||
if (size() <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
getAvatars().remove(slot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void copyFrom(TeamInfo team) {
|
||||
copyFrom(team, GAME_OPTIONS.avatarLimits.singlePlayerTeam);
|
||||
}
|
||||
|
||||
public void copyFrom(TeamInfo team, int maxTeamSize) {
|
||||
// Clone avatar ids from team to copy from
|
||||
List<Integer> avatarIds = new ArrayList<>(team.getAvatars());
|
||||
|
||||
// Clear current avatar list first
|
||||
this.getAvatars().clear();
|
||||
|
||||
// Copy from team
|
||||
int len = Math.min(avatarIds.size(), maxTeamSize);
|
||||
for (int i = 0; i < len; i++) {
|
||||
int id = avatarIds.get(i);
|
||||
this.getAvatars().add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,221 +8,221 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum OpenState {
|
||||
OPEN_STATE_NONE (0),
|
||||
OPEN_STATE_PAIMON (1),
|
||||
OPEN_STATE_PAIMON_NAVIGATION (2),
|
||||
OPEN_STATE_AVATAR_PROMOTE (3),
|
||||
OPEN_STATE_AVATAR_TALENT (4),
|
||||
OPEN_STATE_WEAPON_PROMOTE (5),
|
||||
OPEN_STATE_WEAPON_AWAKEN (6),
|
||||
OPEN_STATE_QUEST_REMIND (7),
|
||||
OPEN_STATE_GAME_GUIDE (8),
|
||||
OPEN_STATE_COOK (9),
|
||||
OPEN_STATE_WEAPON_UPGRADE (10),
|
||||
OPEN_STATE_RELIQUARY_UPGRADE (11),
|
||||
OPEN_STATE_RELIQUARY_PROMOTE (12),
|
||||
OPEN_STATE_WEAPON_PROMOTE_GUIDE (13),
|
||||
OPEN_STATE_WEAPON_CHANGE_GUIDE (14),
|
||||
OPEN_STATE_PLAYER_LVUP_GUIDE (15),
|
||||
OPEN_STATE_FRESHMAN_GUIDE (16),
|
||||
OPEN_STATE_SKIP_FRESHMAN_GUIDE (17),
|
||||
OPEN_STATE_GUIDE_MOVE_CAMERA (18),
|
||||
OPEN_STATE_GUIDE_SCALE_CAMERA (19),
|
||||
OPEN_STATE_GUIDE_KEYBOARD (20),
|
||||
OPEN_STATE_GUIDE_MOVE (21),
|
||||
OPEN_STATE_GUIDE_JUMP (22),
|
||||
OPEN_STATE_GUIDE_SPRINT (23),
|
||||
OPEN_STATE_GUIDE_MAP (24),
|
||||
OPEN_STATE_GUIDE_ATTACK (25),
|
||||
OPEN_STATE_GUIDE_FLY (26),
|
||||
OPEN_STATE_GUIDE_TALENT (27),
|
||||
OPEN_STATE_GUIDE_RELIC (28),
|
||||
OPEN_STATE_GUIDE_RELIC_PROM (29),
|
||||
OPEN_STATE_COMBINE (30, 2),
|
||||
OPEN_STATE_GACHA (31),
|
||||
OPEN_STATE_GUIDE_GACHA (32),
|
||||
OPEN_STATE_GUIDE_TEAM (33),
|
||||
OPEN_STATE_GUIDE_PROUD (34),
|
||||
OPEN_STATE_GUIDE_AVATAR_PROMOTE (35),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_CARD (36),
|
||||
OPEN_STATE_FORGE (37, 2),
|
||||
OPEN_STATE_GUIDE_BAG (38),
|
||||
OPEN_STATE_EXPEDITION (39, 14),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_DAILYTASK (40),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_DUNGEON (41),
|
||||
OPEN_STATE_TOWER (42),
|
||||
OPEN_STATE_WORLD_STAMINA (43),
|
||||
OPEN_STATE_TOWER_FIRST_ENTER (44),
|
||||
OPEN_STATE_RESIN (45),
|
||||
OPEN_STATE_LIMIT_REGION_FRESHMEAT (47),
|
||||
OPEN_STATE_LIMIT_REGION_GLOBAL (48),
|
||||
OPEN_STATE_MULTIPLAYER (49, 16),
|
||||
OPEN_STATE_GUIDE_MOUSEPC (50),
|
||||
OPEN_STATE_GUIDE_MULTIPLAYER (51),
|
||||
OPEN_STATE_GUIDE_DUNGEONREWARD (52),
|
||||
OPEN_STATE_GUIDE_BLOSSOM (53, 8),
|
||||
OPEN_STATE_AVATAR_FASHION (54),
|
||||
OPEN_STATE_PHOTOGRAPH (55),
|
||||
OPEN_STATE_GUIDE_KSLQUEST (56),
|
||||
OPEN_STATE_PERSONAL_LINE (57, 26),
|
||||
OPEN_STATE_GUIDE_PERSONAL_LINE (58),
|
||||
OPEN_STATE_GUIDE_APPEARANCE (59),
|
||||
OPEN_STATE_GUIDE_PROCESS (60),
|
||||
OPEN_STATE_GUIDE_PERSONAL_LINE_KEY (61),
|
||||
OPEN_STATE_GUIDE_WIDGET (62),
|
||||
OPEN_STATE_GUIDE_ACTIVITY_SKILL_ASTER (63),
|
||||
OPEN_STATE_GUIDE_COLDCLIMATE (64),
|
||||
OPEN_STATE_DERIVATIVE_MALL (65),
|
||||
OPEN_STATE_GUIDE_EXITMULTIPLAYER (66),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD (67),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_REBUILD (68),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_CARD (69),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MONSTER (70),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MISSION_CHECK (71),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD_SELECT (72),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_CHALLENGE_START (73),
|
||||
OPEN_STATE_GUIDE_CONVERT (74),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MULTIPLAYER (75),
|
||||
OPEN_STATE_GUIDE_COOP_TASK (76),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_ADEPTIABODE (77),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_DEPLOY (78),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP (79),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION (80),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER (81),
|
||||
OPEN_STATE_GUIDE_HIDEANDSEEK_SKILL (82),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_MAPLIST (83),
|
||||
OPEN_STATE_GUIDE_RELICRESOLVE (84),
|
||||
OPEN_STATE_GUIDE_GGUIDE (85),
|
||||
OPEN_STATE_GUIDE_GGUIDE_HINT (86),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP_V2 (87),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION_V2 (88),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER_V2 (89),
|
||||
OPEN_STATE_GUIDE_QUICK_TEAMMEMBERCHANGE (90), // Mobile only
|
||||
OPEN_STATE_GGUIDE_FIRSTSHOW (91),
|
||||
OPEN_STATE_GGUIDE_MAINPAGE_ENTRY_DISAPPEAR (92),
|
||||
OPEN_STATE_CITY_REPUATION_MENGDE (800),
|
||||
OPEN_STATE_CITY_REPUATION_LIYUE (801),
|
||||
OPEN_STATE_CITY_REPUATION_UI_HINT (802, 25),
|
||||
OPEN_STATE_CITY_REPUATION_INAZUMA (803),
|
||||
OPEN_STATE_SHOP_TYPE_MALL (900),
|
||||
OPEN_STATE_SHOP_TYPE_RECOMMANDED (901, 1),
|
||||
OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL (902, 1),
|
||||
OPEN_STATE_SHOP_TYPE_GIFTPACKAGE (903, 1),
|
||||
OPEN_STATE_SHOP_TYPE_PAIMON (1001),
|
||||
OPEN_STATE_SHOP_TYPE_CITY (1002),
|
||||
OPEN_STATE_SHOP_TYPE_BLACKSMITH (1003),
|
||||
OPEN_STATE_SHOP_TYPE_GROCERY (1004, 5),
|
||||
OPEN_STATE_SHOP_TYPE_FOOD (1005, 5),
|
||||
OPEN_STATE_SHOP_TYPE_SEA_LAMP (1006, 13),
|
||||
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP (1007),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_GROCERY (1008, 5),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_SOUVENIR (1009),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_RESTAURANT (1010, 5),
|
||||
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR (1011),
|
||||
OPEN_STATE_SHOP_TYPE_NPC_TOMOKI (1012),
|
||||
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR_BLACK_BAR (1013),
|
||||
OPEN_ADVENTURE_MANUAL (1100),
|
||||
OPEN_ADVENTURE_MANUAL_CITY_MENGDE (1101),
|
||||
OPEN_ADVENTURE_MANUAL_CITY_LIYUE (1102),
|
||||
OPEN_ADVENTURE_MANUAL_MONSTER (1103, 8),
|
||||
OPEN_ADVENTURE_MANUAL_BOSS_DUNGEON (1104),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP (1200),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP_TAB2 (1201),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP_TAB3 (1202),
|
||||
OPEN_STATE_BATTLE_PASS (1300, 1),
|
||||
OPEN_STATE_BATTLE_PASS_ENTRY (1301, 20),
|
||||
OPEN_STATE_ACTIVITY_CRUCIBLE (1400),
|
||||
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_OPEN (1401),
|
||||
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_CLOSE (1402),
|
||||
OPEN_STATE_ACTIVITY_ENTRY_OPEN (1403),
|
||||
OPEN_STATE_MENGDE_INFUSEDCRYSTAL (1404),
|
||||
OPEN_STATE_LIYUE_INFUSEDCRYSTAL (1405),
|
||||
OPEN_STATE_SNOW_MOUNTAIN_ELDER_TREE (1406),
|
||||
OPEN_STATE_MIRACLE_RING (1407),
|
||||
OPEN_STATE_COOP_LINE (1408, 26),
|
||||
OPEN_STATE_INAZUMA_INFUSEDCRYSTAL (1409),
|
||||
OPEN_STATE_FISH (1410),
|
||||
OPEN_STATE_GUIDE_SUMO_TEAM_SKILL (1411),
|
||||
OPEN_STATE_GUIDE_FISH_RECIPE (1412),
|
||||
OPEN_STATE_HOME (1500),
|
||||
OPEN_STATE_ACTIVITY_HOMEWORLD (1501, 28),
|
||||
OPEN_STATE_ADEPTIABODE (1502),
|
||||
OPEN_STATE_HOME_AVATAR (1503),
|
||||
OPEN_STATE_HOME_EDIT (1504),
|
||||
OPEN_STATE_HOME_EDIT_TIPS (1505),
|
||||
OPEN_STATE_RELIQUARY_DECOMPOSE (1600, 45),
|
||||
OPEN_STATE_ACTIVITY_H5 (1700, 10),
|
||||
OPEN_STATE_ORAIONOKAMI (2000),
|
||||
OPEN_STATE_GUIDE_CHESS_MISSION_CHECK (2001),
|
||||
OPEN_STATE_GUIDE_CHESS_BUILD (2002),
|
||||
OPEN_STATE_GUIDE_CHESS_WIND_TOWER_CIRCLE (2003),
|
||||
OPEN_STATE_GUIDE_CHESS_CARD_SELECT (2004),
|
||||
OPEN_STATE_INAZUMA_MAINQUEST_FINISHED (2005),
|
||||
OPEN_STATE_PAIMON_LVINFO (2100, 7),
|
||||
OPEN_STATE_TELEPORT_HUD (2101, 2),
|
||||
OPEN_STATE_GUIDE_MAP_UNLOCK (2102),
|
||||
OPEN_STATE_GUIDE_PAIMON_LVINFO (2103),
|
||||
OPEN_STATE_GUIDE_AMBORTRANSPORT (2104),
|
||||
OPEN_STATE_GUIDE_FLY_SECOND (2105),
|
||||
OPEN_STATE_GUIDE_KAEYA_CLUE (2106),
|
||||
OPEN_STATE_CAPTURE_CODEX (2107),
|
||||
OPEN_STATE_ACTIVITY_FISH_OPEN (2200),
|
||||
OPEN_STATE_ACTIVITY_FISH_CLOSE (2201),
|
||||
OPEN_STATE_GUIDE_ROGUE_MAP (2205),
|
||||
OPEN_STATE_GUIDE_ROGUE_RUNE (2206),
|
||||
OPEN_STATE_GUIDE_BARTENDER_FORMULA (2210),
|
||||
OPEN_STATE_GUIDE_BARTENDER_MIX (2211),
|
||||
OPEN_STATE_GUIDE_BARTENDER_CUP (2212),
|
||||
OPEN_STATE_GUIDE_MAIL_FAVORITES (2400),
|
||||
OPEN_STATE_GUIDE_POTION_CONFIGURE (2401),
|
||||
OPEN_STATE_GUIDE_LANV2_FIREWORK (2402),
|
||||
OPEN_STATE_LOADINGTIPS_ENKANOMIYA (2403),
|
||||
OPEN_STATE_MICHIAE_CASKET (2500, 30),
|
||||
OPEN_STATE_MAIL_COLLECT_UNLOCK_RED_POINT (2501),
|
||||
OPEN_STATE_LUMEN_STONE (2600),
|
||||
OPEN_STATE_GUIDE_CRYSTALLINK_BUFF (2601),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3 (2700),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_REAL_TIME_EDIT (2701),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_TIMELINE_EDIT (2702),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_SETTING (2703),
|
||||
OPEN_STATE_GUIDE_ROBOTGACHA (2704),
|
||||
OPEN_STATE_GUIDE_FRAGILE_RESIN (2800),
|
||||
OPEN_ADVENTURE_MANUAL_EDUCATION (2801);
|
||||
|
||||
private final int value;
|
||||
private final int unlockLevel;
|
||||
private static final Int2ObjectMap<OpenState> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, OpenState> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private OpenState(int value) {
|
||||
this.value = value;
|
||||
this.unlockLevel = -1;
|
||||
}
|
||||
private OpenState(int value, int unlockLevel) {
|
||||
this.value = value;
|
||||
this.unlockLevel = unlockLevel;
|
||||
}
|
||||
OPEN_STATE_NONE (0),
|
||||
OPEN_STATE_PAIMON (1),
|
||||
OPEN_STATE_PAIMON_NAVIGATION (2),
|
||||
OPEN_STATE_AVATAR_PROMOTE (3),
|
||||
OPEN_STATE_AVATAR_TALENT (4),
|
||||
OPEN_STATE_WEAPON_PROMOTE (5),
|
||||
OPEN_STATE_WEAPON_AWAKEN (6),
|
||||
OPEN_STATE_QUEST_REMIND (7),
|
||||
OPEN_STATE_GAME_GUIDE (8),
|
||||
OPEN_STATE_COOK (9),
|
||||
OPEN_STATE_WEAPON_UPGRADE (10),
|
||||
OPEN_STATE_RELIQUARY_UPGRADE (11),
|
||||
OPEN_STATE_RELIQUARY_PROMOTE (12),
|
||||
OPEN_STATE_WEAPON_PROMOTE_GUIDE (13),
|
||||
OPEN_STATE_WEAPON_CHANGE_GUIDE (14),
|
||||
OPEN_STATE_PLAYER_LVUP_GUIDE (15),
|
||||
OPEN_STATE_FRESHMAN_GUIDE (16),
|
||||
OPEN_STATE_SKIP_FRESHMAN_GUIDE (17),
|
||||
OPEN_STATE_GUIDE_MOVE_CAMERA (18),
|
||||
OPEN_STATE_GUIDE_SCALE_CAMERA (19),
|
||||
OPEN_STATE_GUIDE_KEYBOARD (20),
|
||||
OPEN_STATE_GUIDE_MOVE (21),
|
||||
OPEN_STATE_GUIDE_JUMP (22),
|
||||
OPEN_STATE_GUIDE_SPRINT (23),
|
||||
OPEN_STATE_GUIDE_MAP (24),
|
||||
OPEN_STATE_GUIDE_ATTACK (25),
|
||||
OPEN_STATE_GUIDE_FLY (26),
|
||||
OPEN_STATE_GUIDE_TALENT (27),
|
||||
OPEN_STATE_GUIDE_RELIC (28),
|
||||
OPEN_STATE_GUIDE_RELIC_PROM (29),
|
||||
OPEN_STATE_COMBINE (30, 2),
|
||||
OPEN_STATE_GACHA (31),
|
||||
OPEN_STATE_GUIDE_GACHA (32),
|
||||
OPEN_STATE_GUIDE_TEAM (33),
|
||||
OPEN_STATE_GUIDE_PROUD (34),
|
||||
OPEN_STATE_GUIDE_AVATAR_PROMOTE (35),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_CARD (36),
|
||||
OPEN_STATE_FORGE (37, 2),
|
||||
OPEN_STATE_GUIDE_BAG (38),
|
||||
OPEN_STATE_EXPEDITION (39, 14),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_DAILYTASK (40),
|
||||
OPEN_STATE_GUIDE_ADVENTURE_DUNGEON (41),
|
||||
OPEN_STATE_TOWER (42),
|
||||
OPEN_STATE_WORLD_STAMINA (43),
|
||||
OPEN_STATE_TOWER_FIRST_ENTER (44),
|
||||
OPEN_STATE_RESIN (45),
|
||||
OPEN_STATE_LIMIT_REGION_FRESHMEAT (47),
|
||||
OPEN_STATE_LIMIT_REGION_GLOBAL (48),
|
||||
OPEN_STATE_MULTIPLAYER (49, 16),
|
||||
OPEN_STATE_GUIDE_MOUSEPC (50),
|
||||
OPEN_STATE_GUIDE_MULTIPLAYER (51),
|
||||
OPEN_STATE_GUIDE_DUNGEONREWARD (52),
|
||||
OPEN_STATE_GUIDE_BLOSSOM (53, 8),
|
||||
OPEN_STATE_AVATAR_FASHION (54),
|
||||
OPEN_STATE_PHOTOGRAPH (55),
|
||||
OPEN_STATE_GUIDE_KSLQUEST (56),
|
||||
OPEN_STATE_PERSONAL_LINE (57, 26),
|
||||
OPEN_STATE_GUIDE_PERSONAL_LINE (58),
|
||||
OPEN_STATE_GUIDE_APPEARANCE (59),
|
||||
OPEN_STATE_GUIDE_PROCESS (60),
|
||||
OPEN_STATE_GUIDE_PERSONAL_LINE_KEY (61),
|
||||
OPEN_STATE_GUIDE_WIDGET (62),
|
||||
OPEN_STATE_GUIDE_ACTIVITY_SKILL_ASTER (63),
|
||||
OPEN_STATE_GUIDE_COLDCLIMATE (64),
|
||||
OPEN_STATE_DERIVATIVE_MALL (65),
|
||||
OPEN_STATE_GUIDE_EXITMULTIPLAYER (66),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD (67),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_REBUILD (68),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_CARD (69),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MONSTER (70),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MISSION_CHECK (71),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_BUILD_SELECT (72),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_CHALLENGE_START (73),
|
||||
OPEN_STATE_GUIDE_CONVERT (74),
|
||||
OPEN_STATE_GUIDE_THEATREMACHANICUS_MULTIPLAYER (75),
|
||||
OPEN_STATE_GUIDE_COOP_TASK (76),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_ADEPTIABODE (77),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_DEPLOY (78),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP (79),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION (80),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER (81),
|
||||
OPEN_STATE_GUIDE_HIDEANDSEEK_SKILL (82),
|
||||
OPEN_STATE_GUIDE_HOMEWORLD_MAPLIST (83),
|
||||
OPEN_STATE_GUIDE_RELICRESOLVE (84),
|
||||
OPEN_STATE_GUIDE_GGUIDE (85),
|
||||
OPEN_STATE_GUIDE_GGUIDE_HINT (86),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_EQUIP_V2 (87),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_MP_SOLUTION_V2 (88),
|
||||
OPEN_STATE_GUIDE_CHANNELLERSLAB_POWER_V2 (89),
|
||||
OPEN_STATE_GUIDE_QUICK_TEAMMEMBERCHANGE (90), // Mobile only
|
||||
OPEN_STATE_GGUIDE_FIRSTSHOW (91),
|
||||
OPEN_STATE_GGUIDE_MAINPAGE_ENTRY_DISAPPEAR (92),
|
||||
OPEN_STATE_CITY_REPUATION_MENGDE (800),
|
||||
OPEN_STATE_CITY_REPUATION_LIYUE (801),
|
||||
OPEN_STATE_CITY_REPUATION_UI_HINT (802, 25),
|
||||
OPEN_STATE_CITY_REPUATION_INAZUMA (803),
|
||||
OPEN_STATE_SHOP_TYPE_MALL (900),
|
||||
OPEN_STATE_SHOP_TYPE_RECOMMANDED (901, 1),
|
||||
OPEN_STATE_SHOP_TYPE_GENESISCRYSTAL (902, 1),
|
||||
OPEN_STATE_SHOP_TYPE_GIFTPACKAGE (903, 1),
|
||||
OPEN_STATE_SHOP_TYPE_PAIMON (1001),
|
||||
OPEN_STATE_SHOP_TYPE_CITY (1002),
|
||||
OPEN_STATE_SHOP_TYPE_BLACKSMITH (1003),
|
||||
OPEN_STATE_SHOP_TYPE_GROCERY (1004, 5),
|
||||
OPEN_STATE_SHOP_TYPE_FOOD (1005, 5),
|
||||
OPEN_STATE_SHOP_TYPE_SEA_LAMP (1006, 13),
|
||||
OPEN_STATE_SHOP_TYPE_VIRTUAL_SHOP (1007),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_GROCERY (1008, 5),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_SOUVENIR (1009),
|
||||
OPEN_STATE_SHOP_TYPE_LIYUE_RESTAURANT (1010, 5),
|
||||
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR (1011),
|
||||
OPEN_STATE_SHOP_TYPE_NPC_TOMOKI (1012),
|
||||
OPEN_STATE_SHOP_TYPE_INAZUMA_SOUVENIR_BLACK_BAR (1013),
|
||||
OPEN_ADVENTURE_MANUAL (1100),
|
||||
OPEN_ADVENTURE_MANUAL_CITY_MENGDE (1101),
|
||||
OPEN_ADVENTURE_MANUAL_CITY_LIYUE (1102),
|
||||
OPEN_ADVENTURE_MANUAL_MONSTER (1103, 8),
|
||||
OPEN_ADVENTURE_MANUAL_BOSS_DUNGEON (1104),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP (1200),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP_TAB2 (1201),
|
||||
OPEN_STATE_ACTIVITY_SEALAMP_TAB3 (1202),
|
||||
OPEN_STATE_BATTLE_PASS (1300, 1),
|
||||
OPEN_STATE_BATTLE_PASS_ENTRY (1301, 20),
|
||||
OPEN_STATE_ACTIVITY_CRUCIBLE (1400),
|
||||
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_OPEN (1401),
|
||||
OPEN_STATE_ACTIVITY_NEWBEEBOUNS_CLOSE (1402),
|
||||
OPEN_STATE_ACTIVITY_ENTRY_OPEN (1403),
|
||||
OPEN_STATE_MENGDE_INFUSEDCRYSTAL (1404),
|
||||
OPEN_STATE_LIYUE_INFUSEDCRYSTAL (1405),
|
||||
OPEN_STATE_SNOW_MOUNTAIN_ELDER_TREE (1406),
|
||||
OPEN_STATE_MIRACLE_RING (1407),
|
||||
OPEN_STATE_COOP_LINE (1408, 26),
|
||||
OPEN_STATE_INAZUMA_INFUSEDCRYSTAL (1409),
|
||||
OPEN_STATE_FISH (1410),
|
||||
OPEN_STATE_GUIDE_SUMO_TEAM_SKILL (1411),
|
||||
OPEN_STATE_GUIDE_FISH_RECIPE (1412),
|
||||
OPEN_STATE_HOME (1500),
|
||||
OPEN_STATE_ACTIVITY_HOMEWORLD (1501, 28),
|
||||
OPEN_STATE_ADEPTIABODE (1502),
|
||||
OPEN_STATE_HOME_AVATAR (1503),
|
||||
OPEN_STATE_HOME_EDIT (1504),
|
||||
OPEN_STATE_HOME_EDIT_TIPS (1505),
|
||||
OPEN_STATE_RELIQUARY_DECOMPOSE (1600, 45),
|
||||
OPEN_STATE_ACTIVITY_H5 (1700, 10),
|
||||
OPEN_STATE_ORAIONOKAMI (2000),
|
||||
OPEN_STATE_GUIDE_CHESS_MISSION_CHECK (2001),
|
||||
OPEN_STATE_GUIDE_CHESS_BUILD (2002),
|
||||
OPEN_STATE_GUIDE_CHESS_WIND_TOWER_CIRCLE (2003),
|
||||
OPEN_STATE_GUIDE_CHESS_CARD_SELECT (2004),
|
||||
OPEN_STATE_INAZUMA_MAINQUEST_FINISHED (2005),
|
||||
OPEN_STATE_PAIMON_LVINFO (2100, 7),
|
||||
OPEN_STATE_TELEPORT_HUD (2101, 2),
|
||||
OPEN_STATE_GUIDE_MAP_UNLOCK (2102),
|
||||
OPEN_STATE_GUIDE_PAIMON_LVINFO (2103),
|
||||
OPEN_STATE_GUIDE_AMBORTRANSPORT (2104),
|
||||
OPEN_STATE_GUIDE_FLY_SECOND (2105),
|
||||
OPEN_STATE_GUIDE_KAEYA_CLUE (2106),
|
||||
OPEN_STATE_CAPTURE_CODEX (2107),
|
||||
OPEN_STATE_ACTIVITY_FISH_OPEN (2200),
|
||||
OPEN_STATE_ACTIVITY_FISH_CLOSE (2201),
|
||||
OPEN_STATE_GUIDE_ROGUE_MAP (2205),
|
||||
OPEN_STATE_GUIDE_ROGUE_RUNE (2206),
|
||||
OPEN_STATE_GUIDE_BARTENDER_FORMULA (2210),
|
||||
OPEN_STATE_GUIDE_BARTENDER_MIX (2211),
|
||||
OPEN_STATE_GUIDE_BARTENDER_CUP (2212),
|
||||
OPEN_STATE_GUIDE_MAIL_FAVORITES (2400),
|
||||
OPEN_STATE_GUIDE_POTION_CONFIGURE (2401),
|
||||
OPEN_STATE_GUIDE_LANV2_FIREWORK (2402),
|
||||
OPEN_STATE_LOADINGTIPS_ENKANOMIYA (2403),
|
||||
OPEN_STATE_MICHIAE_CASKET (2500, 30),
|
||||
OPEN_STATE_MAIL_COLLECT_UNLOCK_RED_POINT (2501),
|
||||
OPEN_STATE_LUMEN_STONE (2600),
|
||||
OPEN_STATE_GUIDE_CRYSTALLINK_BUFF (2601),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3 (2700),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_REAL_TIME_EDIT (2701),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_TIMELINE_EDIT (2702),
|
||||
OPEN_STATE_GUIDE_MUSIC_GAME_V3_SETTING (2703),
|
||||
OPEN_STATE_GUIDE_ROBOTGACHA (2704),
|
||||
OPEN_STATE_GUIDE_FRAGILE_RESIN (2800),
|
||||
OPEN_ADVENTURE_MANUAL_EDUCATION (2801);
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
private final int value;
|
||||
private final int unlockLevel;
|
||||
private static final Int2ObjectMap<OpenState> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, OpenState> stringMap = new HashMap<>();
|
||||
|
||||
public int getUnlockLevel() {
|
||||
return this.unlockLevel;
|
||||
}
|
||||
|
||||
public static OpenState getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, OPEN_STATE_NONE);
|
||||
}
|
||||
|
||||
public static OpenState getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, OPEN_STATE_NONE);
|
||||
}
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private OpenState(int value) {
|
||||
this.value = value;
|
||||
this.unlockLevel = -1;
|
||||
}
|
||||
private OpenState(int value, int unlockLevel) {
|
||||
this.value = value;
|
||||
this.unlockLevel = unlockLevel;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getUnlockLevel() {
|
||||
return this.unlockLevel;
|
||||
}
|
||||
|
||||
public static OpenState getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, OPEN_STATE_NONE);
|
||||
}
|
||||
|
||||
public static OpenState getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, OPEN_STATE_NONE);
|
||||
}
|
||||
}
|
||||
|
@ -22,47 +22,47 @@ import emu.grasscutter.utils.Utils;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient private GameMainQuest mainQuest;
|
||||
@Transient private QuestData questData;
|
||||
@Transient private GameMainQuest mainQuest;
|
||||
@Transient private QuestData questData;
|
||||
|
||||
private int questId;
|
||||
private int mainQuestId;
|
||||
private QuestState state;
|
||||
private int questId;
|
||||
private int mainQuestId;
|
||||
private QuestState state;
|
||||
|
||||
private int startTime;
|
||||
private int acceptTime;
|
||||
private int finishTime;
|
||||
private int startTime;
|
||||
private int acceptTime;
|
||||
private int finishTime;
|
||||
|
||||
private int[] finishProgressList;
|
||||
private int[] failProgressList;
|
||||
private int[] finishProgressList;
|
||||
private int[] failProgressList;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.questId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.questId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
|
||||
if (questData.getFinishCond() != null && questData.getAcceptCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
if (questData.getFinishCond() != null && questData.getAcceptCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
|
||||
this.mainQuest.getChildQuests().put(this.questId, this);
|
||||
this.mainQuest.getChildQuests().put(this.questId, this);
|
||||
|
||||
this.getData().getBeginExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
|
||||
this.getOwner().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL, this.questId, this.state.getValue());
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(questId)){
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(questId)) {
|
||||
mainQuest.getOwner().sendPacket(new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(questId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN
|
||||
@ -70,112 +70,112 @@ public class GameQuest {
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is started", questId);
|
||||
}
|
||||
}
|
||||
|
||||
public GameMainQuest getMainQuest() {
|
||||
return mainQuest;
|
||||
}
|
||||
public GameMainQuest getMainQuest() {
|
||||
return mainQuest;
|
||||
}
|
||||
|
||||
public void setMainQuest(GameMainQuest mainQuest) {
|
||||
this.mainQuest = mainQuest;
|
||||
}
|
||||
public void setMainQuest(GameMainQuest mainQuest) {
|
||||
this.mainQuest = mainQuest;
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return getMainQuest().getOwner();
|
||||
}
|
||||
public Player getOwner() {
|
||||
return getMainQuest().getOwner();
|
||||
}
|
||||
|
||||
public int getQuestId() {
|
||||
return questId;
|
||||
}
|
||||
public int getQuestId() {
|
||||
return questId;
|
||||
}
|
||||
|
||||
public int getMainQuestId() {
|
||||
return mainQuestId;
|
||||
}
|
||||
public int getMainQuestId() {
|
||||
return mainQuestId;
|
||||
}
|
||||
|
||||
public QuestData getData() {
|
||||
return questData;
|
||||
}
|
||||
public QuestData getData() {
|
||||
return questData;
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (this.getQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
public void setConfig(QuestData config) {
|
||||
if (this.getQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
public QuestState getState() {
|
||||
return state;
|
||||
}
|
||||
public QuestState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(QuestState state) {
|
||||
this.state = state;
|
||||
}
|
||||
public void setState(QuestState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public int getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
public int getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(int startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
public void setStartTime(int startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public int getAcceptTime() {
|
||||
return acceptTime;
|
||||
}
|
||||
public int getAcceptTime() {
|
||||
return acceptTime;
|
||||
}
|
||||
|
||||
public void setAcceptTime(int acceptTime) {
|
||||
this.acceptTime = acceptTime;
|
||||
}
|
||||
public void setAcceptTime(int acceptTime) {
|
||||
this.acceptTime = acceptTime;
|
||||
}
|
||||
|
||||
public int getFinishTime() {
|
||||
return finishTime;
|
||||
}
|
||||
public int getFinishTime() {
|
||||
return finishTime;
|
||||
}
|
||||
|
||||
public void setFinishTime(int finishTime) {
|
||||
this.finishTime = finishTime;
|
||||
}
|
||||
public void setFinishTime(int finishTime) {
|
||||
this.finishTime = finishTime;
|
||||
}
|
||||
|
||||
public int[] getFinishProgressList() {
|
||||
return finishProgressList;
|
||||
}
|
||||
public int[] getFinishProgressList() {
|
||||
return finishProgressList;
|
||||
}
|
||||
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
|
||||
public int[] getFailProgressList() {
|
||||
return failProgressList;
|
||||
}
|
||||
public int[] getFailProgressList() {
|
||||
return failProgressList;
|
||||
}
|
||||
|
||||
public void setFailProgress(int index, int value) {
|
||||
failProgressList[index] = value;
|
||||
}
|
||||
public void setFailProgress(int index, int value) {
|
||||
failProgressList[index] = value;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
public void finish() {
|
||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
if (this.getFinishProgressList() != null) {
|
||||
for (int i = 0 ; i < getFinishProgressList().length; i++) {
|
||||
getFinishProgressList()[i] = 1;
|
||||
}
|
||||
}
|
||||
if (this.getFinishProgressList() != null) {
|
||||
for (int i = 0 ; i < getFinishProgressList().length; i++) {
|
||||
getFinishProgressList()[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this));
|
||||
this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this));
|
||||
this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this));
|
||||
this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (this.getData().finishParent()) {
|
||||
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
|
||||
this.getMainQuest().finish();
|
||||
} else {
|
||||
// Try and accept other quests if possible
|
||||
this.tryAcceptQuestLine();
|
||||
this.save();
|
||||
}
|
||||
if (this.getData().finishParent()) {
|
||||
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
|
||||
this.getMainQuest().finish();
|
||||
} else {
|
||||
// Try and accept other quests if possible
|
||||
this.tryAcceptQuestLine();
|
||||
this.save();
|
||||
}
|
||||
|
||||
this.getData().getFinishExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
|
||||
this.getOwner().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL, this.questId, this.state.getValue());
|
||||
|
||||
if (ChapterData.endQuestChapterMap.containsKey(questId)){
|
||||
if (ChapterData.endQuestChapterMap.containsKey(questId)) {
|
||||
mainQuest.getOwner().sendPacket(new PacketChapterStateNotify(
|
||||
ChapterData.endQuestChapterMap.get(questId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END
|
||||
@ -183,74 +183,74 @@ public class GameQuest {
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is finished", questId);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryAcceptQuestLine() {
|
||||
try {
|
||||
MainQuestData questConfig = GameData.getMainQuestDataMap().get(this.getMainQuestId());
|
||||
public boolean tryAcceptQuestLine() {
|
||||
try {
|
||||
MainQuestData questConfig = GameData.getMainQuestDataMap().get(this.getMainQuestId());
|
||||
|
||||
for (SubQuestData subQuest : questConfig.getSubQuests()) {
|
||||
GameQuest quest = getMainQuest().getChildQuestById(subQuest.getSubId());
|
||||
for (SubQuestData subQuest : questConfig.getSubQuests()) {
|
||||
GameQuest quest = getMainQuest().getChildQuestById(subQuest.getSubId());
|
||||
|
||||
if (quest == null) {
|
||||
QuestData questData = GameData.getQuestDataMap().get(subQuest.getSubId());
|
||||
if (quest == null) {
|
||||
QuestData questData = GameData.getQuestDataMap().get(subQuest.getSubId());
|
||||
|
||||
if (questData == null || questData.getAcceptCond() == null
|
||||
|| questData.getAcceptCond().size() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (questData == null || questData.getAcceptCond() == null
|
||||
|| questData.getAcceptCond().size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int[] accept = new int[questData.getAcceptCond().size()];
|
||||
int[] accept = new int[questData.getAcceptCond().size()];
|
||||
|
||||
// TODO
|
||||
for (int i = 0; i < questData.getAcceptCond().size(); i++) {
|
||||
QuestCondition condition = questData.getAcceptCond().get(i);
|
||||
boolean result = getOwner().getServer().getQuestSystem().triggerCondition(this, condition,
|
||||
// TODO
|
||||
for (int i = 0; i < questData.getAcceptCond().size(); i++) {
|
||||
QuestCondition condition = questData.getAcceptCond().get(i);
|
||||
boolean result = getOwner().getServer().getQuestSystem().triggerCondition(this, condition,
|
||||
condition.getParamStr(),
|
||||
condition.getParam());
|
||||
condition.getParam());
|
||||
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
|
||||
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
|
||||
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
|
||||
|
||||
if (shouldAccept) {
|
||||
this.getOwner().getQuestManager().addQuest(questData.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
|
||||
}
|
||||
if (shouldAccept) {
|
||||
this.getOwner().getQuestManager().addQuest(questData.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
public void save() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto = Quest.newBuilder()
|
||||
.setQuestId(this.getQuestId())
|
||||
.setState(this.getState().getValue())
|
||||
.setParentQuestId(this.getMainQuestId())
|
||||
.setStartTime(this.getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(this.getAcceptTime());
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto = Quest.newBuilder()
|
||||
.setQuestId(this.getQuestId())
|
||||
.setState(this.getState().getValue())
|
||||
.setParentQuestId(this.getMainQuestId())
|
||||
.setStartTime(this.getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(this.getAcceptTime());
|
||||
|
||||
if (this.getFinishProgressList() != null) {
|
||||
for (int i : this.getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
if (this.getFinishProgressList() != null) {
|
||||
for (int i : this.getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getFailProgressList() != null) {
|
||||
for (int i : this.getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
if (this.getFailProgressList() != null) {
|
||||
for (int i : this.getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
|
@ -21,106 +21,106 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class QuestManager extends BasePlayerManager {
|
||||
private final Int2ObjectMap<GameMainQuest> quests;
|
||||
private final Int2ObjectMap<GameMainQuest> quests;
|
||||
|
||||
public QuestManager(Player player) {
|
||||
super(player);
|
||||
this.quests = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
public QuestManager(Player player) {
|
||||
super(player);
|
||||
this.quests = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public Int2ObjectMap<GameMainQuest> getQuests() {
|
||||
return quests;
|
||||
}
|
||||
|
||||
public GameMainQuest getMainQuestById(int mainQuestId) {
|
||||
return getQuests().get(mainQuestId);
|
||||
}
|
||||
|
||||
public GameQuest getQuestById(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
if (questConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GameMainQuest mainQuest = getQuests().get(questConfig.getMainId());
|
||||
|
||||
if (mainQuest == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mainQuest.getChildQuests().get(questId);
|
||||
}
|
||||
|
||||
public void forEachQuest(Consumer<GameQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
callback.accept(quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
public Int2ObjectMap<GameMainQuest> getQuests() {
|
||||
return quests;
|
||||
}
|
||||
|
||||
public void forEachMainQuest(Consumer<GameMainQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
callback.accept(mainQuest);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void forEachActiveQuest(Consumer<GameQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
if (quest.getState() != QuestState.QUEST_STATE_FINISHED) {
|
||||
callback.accept(quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameMainQuest addMainQuest(QuestData questConfig) {
|
||||
GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId());
|
||||
getQuests().put(mainQuest.getParentQuestId(), mainQuest);
|
||||
public GameMainQuest getMainQuestById(int mainQuestId) {
|
||||
return getQuests().get(mainQuestId);
|
||||
}
|
||||
|
||||
getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));
|
||||
|
||||
return mainQuest;
|
||||
}
|
||||
|
||||
public GameQuest addQuest(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
if (questConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Main quest
|
||||
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
||||
|
||||
// Create main quest if it doesnt exist
|
||||
if (mainQuest == null) {
|
||||
mainQuest = addMainQuest(questConfig);
|
||||
}
|
||||
|
||||
// Sub quest
|
||||
GameQuest quest = mainQuest.getChildQuestById(questId);
|
||||
|
||||
if (quest != null) {
|
||||
return null;
|
||||
}
|
||||
public GameQuest getQuestById(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
if (questConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create
|
||||
quest = new GameQuest(mainQuest, questConfig);
|
||||
GameMainQuest mainQuest = getQuests().get(questConfig.getMainId());
|
||||
|
||||
// Save main quest
|
||||
mainQuest.save();
|
||||
if (mainQuest == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Send packet
|
||||
getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest));
|
||||
return mainQuest.getChildQuests().get(questId);
|
||||
}
|
||||
|
||||
return quest;
|
||||
}
|
||||
public void startMainQuest(int mainQuestId){
|
||||
public void forEachQuest(Consumer<GameQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
callback.accept(quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void forEachMainQuest(Consumer<GameMainQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
callback.accept(mainQuest);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void forEachActiveQuest(Consumer<GameQuest> callback) {
|
||||
for (GameMainQuest mainQuest : getQuests().values()) {
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
if (quest.getState() != QuestState.QUEST_STATE_FINISHED) {
|
||||
callback.accept(quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public GameMainQuest addMainQuest(QuestData questConfig) {
|
||||
GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId());
|
||||
getQuests().put(mainQuest.getParentQuestId(), mainQuest);
|
||||
|
||||
getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));
|
||||
|
||||
return mainQuest;
|
||||
}
|
||||
|
||||
public GameQuest addQuest(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
if (questConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Main quest
|
||||
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
||||
|
||||
// Create main quest if it doesnt exist
|
||||
if (mainQuest == null) {
|
||||
mainQuest = addMainQuest(questConfig);
|
||||
}
|
||||
|
||||
// Sub quest
|
||||
GameQuest quest = mainQuest.getChildQuestById(questId);
|
||||
|
||||
if (quest != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create
|
||||
quest = new GameQuest(mainQuest, questConfig);
|
||||
|
||||
// Save main quest
|
||||
mainQuest.save();
|
||||
|
||||
// Send packet
|
||||
getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest));
|
||||
|
||||
return quest;
|
||||
}
|
||||
public void startMainQuest(int mainQuestId) {
|
||||
var mainQuestData = GameData.getMainQuestDataMap().get(mainQuestId);
|
||||
|
||||
if (mainQuestData == null){
|
||||
if (mainQuestData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -133,52 +133,52 @@ public class QuestManager extends BasePlayerManager {
|
||||
triggerEvent(condType, "", params);
|
||||
}
|
||||
|
||||
public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
|
||||
public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
Set<GameQuest> changedQuests = new HashSet<>();
|
||||
Set<GameQuest> changedQuests = new HashSet<>();
|
||||
|
||||
this.forEachActiveQuest(quest -> {
|
||||
QuestData data = quest.getData();
|
||||
this.forEachActiveQuest(quest -> {
|
||||
QuestData data = quest.getData();
|
||||
|
||||
for (int i = 0; i < data.getFinishCond().size(); i++) {
|
||||
if (quest.getFinishProgressList() == null
|
||||
|| quest.getFinishProgressList().length == 0
|
||||
|| quest.getFinishProgressList()[i] == 1) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < data.getFinishCond().size(); i++) {
|
||||
if (quest.getFinishProgressList() == null
|
||||
|| quest.getFinishProgressList().length == 0
|
||||
|| quest.getFinishProgressList()[i] == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QuestCondition condition = data.getFinishCond().get(i);
|
||||
QuestCondition condition = data.getFinishCond().get(i);
|
||||
|
||||
if (condition.getType() != condType) {
|
||||
continue;
|
||||
}
|
||||
if (condition.getType() != condType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean result = getPlayer().getServer().getQuestSystem().triggerContent(quest, condition, paramStr, params);
|
||||
boolean result = getPlayer().getServer().getQuestSystem().triggerContent(quest, condition, paramStr, params);
|
||||
|
||||
if (result) {
|
||||
quest.getFinishProgressList()[i] = 1;
|
||||
if (result) {
|
||||
quest.getFinishProgressList()[i] = 1;
|
||||
|
||||
changedQuests.add(quest);
|
||||
}
|
||||
}
|
||||
});
|
||||
changedQuests.add(quest);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (GameQuest quest : changedQuests) {
|
||||
LogicType logicType = quest.getData().getFailCondComb();
|
||||
int[] progress = quest.getFinishProgressList();
|
||||
for (GameQuest quest : changedQuests) {
|
||||
LogicType logicType = quest.getData().getFailCondComb();
|
||||
int[] progress = quest.getFinishProgressList();
|
||||
|
||||
// Handle logical comb
|
||||
boolean finish = LogicType.calculate(logicType, progress);
|
||||
// Handle logical comb
|
||||
boolean finish = LogicType.calculate(logicType, progress);
|
||||
|
||||
// Finish
|
||||
if (finish) {
|
||||
quest.finish();
|
||||
} else {
|
||||
getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest));
|
||||
quest.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Finish
|
||||
if (finish) {
|
||||
quest.finish();
|
||||
} else {
|
||||
getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest));
|
||||
quest.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<QuestGroupSuite> getSceneGroupSuite(int sceneId) {
|
||||
return getQuests().values().stream()
|
||||
@ -189,18 +189,18 @@ public class QuestManager extends BasePlayerManager {
|
||||
.filter(i -> i.getScene() == sceneId)
|
||||
.toList();
|
||||
}
|
||||
public void loadFromDatabase() {
|
||||
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
|
||||
|
||||
for (GameMainQuest mainQuest : quests) {
|
||||
mainQuest.setOwner(this.getPlayer());
|
||||
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
quest.setMainQuest(mainQuest);
|
||||
quest.setConfig(GameData.getQuestDataMap().get(quest.getQuestId()));
|
||||
}
|
||||
|
||||
this.getQuests().put(mainQuest.getParentQuestId(), mainQuest);
|
||||
}
|
||||
}
|
||||
public void loadFromDatabase() {
|
||||
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
|
||||
|
||||
for (GameMainQuest mainQuest : quests) {
|
||||
mainQuest.setOwner(this.getPlayer());
|
||||
|
||||
for (GameQuest quest : mainQuest.getChildQuests().values()) {
|
||||
quest.setMainQuest(mainQuest);
|
||||
quest.setConfig(GameData.getQuestDataMap().get(quest.getQuestId()));
|
||||
}
|
||||
|
||||
this.getQuests().put(mainQuest.getParentQuestId(), mainQuest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,81 +17,81 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class QuestSystem extends BaseGameSystem {
|
||||
private final Int2ObjectMap<QuestBaseHandler> condHandlers;
|
||||
private final Int2ObjectMap<QuestBaseHandler> contHandlers;
|
||||
private final Int2ObjectMap<QuestExecHandler> execHandlers;
|
||||
private final Int2ObjectMap<QuestBaseHandler> condHandlers;
|
||||
private final Int2ObjectMap<QuestBaseHandler> contHandlers;
|
||||
private final Int2ObjectMap<QuestExecHandler> execHandlers;
|
||||
|
||||
public QuestSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
this.condHandlers = new Int2ObjectOpenHashMap<>();
|
||||
this.contHandlers = new Int2ObjectOpenHashMap<>();
|
||||
this.execHandlers = new Int2ObjectOpenHashMap<>();
|
||||
public QuestSystem(GameServer server) {
|
||||
super(server);
|
||||
|
||||
this.registerHandlers();
|
||||
}
|
||||
this.condHandlers = new Int2ObjectOpenHashMap<>();
|
||||
this.contHandlers = new Int2ObjectOpenHashMap<>();
|
||||
this.execHandlers = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public void registerHandlers() {
|
||||
this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions", QuestBaseHandler.class);
|
||||
this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content", QuestBaseHandler.class);
|
||||
this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
|
||||
}
|
||||
this.registerHandlers();
|
||||
}
|
||||
|
||||
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
|
||||
Reflections reflections = new Reflections(packageName);
|
||||
var handlerClasses = reflections.getSubTypesOf(clazz);
|
||||
public void registerHandlers() {
|
||||
this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions", QuestBaseHandler.class);
|
||||
this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content", QuestBaseHandler.class);
|
||||
this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
|
||||
}
|
||||
|
||||
for (var obj : handlerClasses) {
|
||||
this.registerPacketHandler(map, obj);
|
||||
}
|
||||
}
|
||||
public <T> void registerHandlers(Int2ObjectMap<T> map, String packageName, Class<T> clazz) {
|
||||
Reflections reflections = new Reflections(packageName);
|
||||
var handlerClasses = reflections.getSubTypesOf(clazz);
|
||||
|
||||
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
|
||||
try {
|
||||
QuestValue opcode = handlerClass.getAnnotation(QuestValue.class);
|
||||
for (var obj : handlerClasses) {
|
||||
this.registerPacketHandler(map, obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == null || opcode.value().getValue() <= 0) {
|
||||
return;
|
||||
}
|
||||
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
|
||||
try {
|
||||
QuestValue opcode = handlerClass.getAnnotation(QuestValue.class);
|
||||
|
||||
map.put(opcode.value().getValue(), handlerClass.newInstance());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (opcode == null || opcode.value().getValue() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO make cleaner
|
||||
map.put(opcode.value().getValue(), handlerClass.newInstance());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean triggerCondition(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = condHandlers.get(condition.getType().getValue());
|
||||
// TODO make cleaner
|
||||
|
||||
if (handler == null || quest.getData() == null) {
|
||||
public boolean triggerCondition(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = condHandlers.get(condition.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getData() == null) {
|
||||
Grasscutter.getLogger().debug("Could not trigger condition {} at {}", condition.getType().getValue(), quest.getData());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
}
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
}
|
||||
|
||||
public boolean triggerContent(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = contHandlers.get(condition.getType().getValue());
|
||||
public boolean triggerContent(GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = contHandlers.get(condition.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getData() == null) {
|
||||
if (handler == null || quest.getData() == null) {
|
||||
Grasscutter.getLogger().debug("Could not trigger content {} at {}", condition.getType().getValue(), quest.getData());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
}
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
}
|
||||
|
||||
public boolean triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
|
||||
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
|
||||
public boolean triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
|
||||
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getData() == null) {
|
||||
if (handler == null || quest.getData() == null) {
|
||||
Grasscutter.getLogger().debug("Could not trigger exec {} at {}", execParam.getType().getValue(), quest.getData());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler.execute(quest, execParam, params);
|
||||
}
|
||||
return handler.execute(quest, execParam, params);
|
||||
}
|
||||
}
|
||||
|
@ -23,22 +23,22 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ShopSystem extends BaseGameSystem {
|
||||
private final Int2ObjectMap<List<ShopInfo>> shopData;
|
||||
private final List<ShopChestTable> shopChestData;
|
||||
private final List<ShopChestBatchUseTable> shopChestBatchUseData;
|
||||
|
||||
private final Int2ObjectMap<List<ShopInfo>> shopData;
|
||||
private final List<ShopChestTable> shopChestData;
|
||||
private final List<ShopChestBatchUseTable> shopChestBatchUseData;
|
||||
|
||||
private static final int REFRESH_HOUR = 4; // In GMT+8 server
|
||||
private static final String TIME_ZONE = "Asia/Shanghai"; // GMT+8 Timezone
|
||||
|
||||
public ShopSystem(GameServer server) {
|
||||
super(server);
|
||||
this.shopData = new Int2ObjectOpenHashMap<>();
|
||||
this.shopChestData = new ArrayList<>();
|
||||
this.shopChestBatchUseData = new ArrayList<>();
|
||||
this.load();
|
||||
}
|
||||
|
||||
public Int2ObjectMap<List<ShopInfo>> getShopData() {
|
||||
public ShopSystem(GameServer server) {
|
||||
super(server);
|
||||
this.shopData = new Int2ObjectOpenHashMap<>();
|
||||
this.shopChestData = new ArrayList<>();
|
||||
this.shopChestBatchUseData = new ArrayList<>();
|
||||
this.load();
|
||||
}
|
||||
|
||||
public Int2ObjectMap<List<ShopInfo>> getShopData() {
|
||||
return shopData;
|
||||
}
|
||||
|
||||
@ -50,96 +50,96 @@ public class ShopSystem extends BaseGameSystem {
|
||||
return shopChestBatchUseData;
|
||||
}
|
||||
|
||||
public static int getShopNextRefreshTime(ShopInfo shopInfo) {
|
||||
return switch (shopInfo.getShopRefreshType()) {
|
||||
case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
case SHOP_REFRESH_WEEKLY -> Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
public static int getShopNextRefreshTime(ShopInfo shopInfo) {
|
||||
return switch (shopInfo.getShopRefreshType()) {
|
||||
case SHOP_REFRESH_DAILY -> Utils.getNextTimestampOfThisHour(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
case SHOP_REFRESH_WEEKLY -> Utils.getNextTimestampOfThisHourInNextWeek(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
case SHOP_REFRESH_MONTHLY -> Utils.getNextTimestampOfThisHourInNextMonth(REFRESH_HOUR, TIME_ZONE, shopInfo.getShopRefreshParam());
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private void loadShop() {
|
||||
try (Reader fileReader = DataLoader.loadReader("Shop.json")) {
|
||||
getShopData().clear();
|
||||
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
|
||||
if(banners.size() > 0) {
|
||||
for (ShopTable shopTable : banners) {
|
||||
for (ShopInfo cost : shopTable.getItems()) {
|
||||
if (cost.getCostItemList() != null) {
|
||||
Iterator<ItemParamData> iterator = cost.getCostItemList().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ItemParamData ipd = iterator.next();
|
||||
if (ipd.getId() == 201) {
|
||||
cost.setHcoin(cost.getHcoin() + ipd.getCount());
|
||||
iterator.remove();
|
||||
}
|
||||
if (ipd.getId() == 203) {
|
||||
cost.setMcoin(cost.getMcoin() + ipd.getCount());
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
getShopData().put(shopTable.getShopId(), shopTable.getItems());
|
||||
}
|
||||
Grasscutter.getLogger().debug("Shop data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0.");
|
||||
}
|
||||
private void loadShop() {
|
||||
try (Reader fileReader = DataLoader.loadReader("Shop.json")) {
|
||||
getShopData().clear();
|
||||
List<ShopTable> banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType());
|
||||
if (banners.size() > 0) {
|
||||
for (ShopTable shopTable : banners) {
|
||||
for (ShopInfo cost : shopTable.getItems()) {
|
||||
if (cost.getCostItemList() != null) {
|
||||
Iterator<ItemParamData> iterator = cost.getCostItemList().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ItemParamData ipd = iterator.next();
|
||||
if (ipd.getId() == 201) {
|
||||
cost.setHcoin(cost.getHcoin() + ipd.getCount());
|
||||
iterator.remove();
|
||||
}
|
||||
if (ipd.getId() == 203) {
|
||||
cost.setMcoin(cost.getMcoin() + ipd.getCount());
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
getShopData().put(shopTable.getShopId(), shopTable.getItems());
|
||||
}
|
||||
Grasscutter.getLogger().debug("Shop data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0.");
|
||||
}
|
||||
|
||||
if (GAME_OPTIONS.enableShopItems) {
|
||||
GameData.getShopGoodsDataEntries().forEach((k, v) -> {
|
||||
if (!getShopData().containsKey(k.intValue()))
|
||||
getShopData().put(k.intValue(), new ArrayList<>());
|
||||
for (ShopGoodsData sgd : v) {
|
||||
var shopInfo = new ShopInfo(sgd);
|
||||
getShopData().get(k.intValue()).add(shopInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load shop data.", e);
|
||||
}
|
||||
}
|
||||
if (GAME_OPTIONS.enableShopItems) {
|
||||
GameData.getShopGoodsDataEntries().forEach((k, v) -> {
|
||||
if (!getShopData().containsKey(k.intValue()))
|
||||
getShopData().put(k.intValue(), new ArrayList<>());
|
||||
for (ShopGoodsData sgd : v) {
|
||||
var shopInfo = new ShopInfo(sgd);
|
||||
getShopData().get(k.intValue()).add(shopInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load shop data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadShopChest() {
|
||||
try (Reader fileReader = DataLoader.loadReader("ShopChest.json")) {
|
||||
getShopChestData().clear();
|
||||
List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
|
||||
if (shopChestTableList.size() > 0) {
|
||||
getShopChestData().addAll(shopChestTableList);
|
||||
Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
|
||||
}
|
||||
}
|
||||
private void loadShopChest() {
|
||||
try (Reader fileReader = DataLoader.loadReader("ShopChest.json")) {
|
||||
getShopChestData().clear();
|
||||
List<ShopChestTable> shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType());
|
||||
if (shopChestTableList.size() > 0) {
|
||||
getShopChestData().addAll(shopChestTableList);
|
||||
Grasscutter.getLogger().debug("ShopChest data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data. ShopChest data size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChest data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadShopChestBatchUse() {
|
||||
try (Reader fileReader = DataLoader.loadReader("ShopChestBatchUse.json")) {
|
||||
getShopChestBatchUseData().clear();
|
||||
List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
|
||||
if (shopChestBatchUseTableList.size() > 0) {
|
||||
getShopChestBatchUseData().addAll(shopChestBatchUseTableList);
|
||||
Grasscutter.getLogger().debug("ShopChestBatchUse data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data. ShopChestBatchUse data size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data.", e);
|
||||
}
|
||||
}
|
||||
private void loadShopChestBatchUse() {
|
||||
try (Reader fileReader = DataLoader.loadReader("ShopChestBatchUse.json")) {
|
||||
getShopChestBatchUseData().clear();
|
||||
List<ShopChestBatchUseTable> shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType());
|
||||
if (shopChestBatchUseTableList.size() > 0) {
|
||||
getShopChestBatchUseData().addAll(shopChestBatchUseTableList);
|
||||
Grasscutter.getLogger().debug("ShopChestBatchUse data successfully loaded.");
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data. ShopChestBatchUse data size is 0.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load ShopChestBatchUse data.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void load() {
|
||||
loadShop();
|
||||
loadShopChest();
|
||||
loadShopChestBatchUse();
|
||||
}
|
||||
public synchronized void load() {
|
||||
loadShop();
|
||||
loadShopChest();
|
||||
loadShopChestBatchUse();
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ import java.util.*;
|
||||
@Getter
|
||||
public class AnnouncementSystem extends BaseGameSystem {
|
||||
private final Map<Integer, AnnounceConfigItem> announceConfigItemMap;
|
||||
|
||||
public AnnouncementSystem(GameServer server){
|
||||
|
||||
public AnnouncementSystem(GameServer server) {
|
||||
super(server);
|
||||
this.announceConfigItemMap = new HashMap<>();
|
||||
loadConfig();
|
||||
@ -51,7 +51,7 @@ public class AnnouncementSystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
public void broadcast(List<AnnounceConfigItem> tpl) {
|
||||
if(tpl == null || tpl.size() == 0){
|
||||
if (tpl == null || tpl.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ public class AnnouncementSystem extends BaseGameSystem {
|
||||
boolean tick;
|
||||
int interval;
|
||||
|
||||
public AnnounceDataOuterClass.AnnounceData.Builder toProto(){
|
||||
public AnnounceDataOuterClass.AnnounceData.Builder toProto() {
|
||||
var proto = AnnounceDataOuterClass.AnnounceData.newBuilder();
|
||||
|
||||
proto.setConfigId(templateId)
|
||||
@ -91,11 +91,11 @@ public class AnnouncementSystem extends BaseGameSystem {
|
||||
.setBeginTime(Utils.getCurrentSeconds() + 1)
|
||||
.setEndTime(Utils.getCurrentSeconds() + 10);
|
||||
|
||||
if(type == AnnounceType.CENTER){
|
||||
if (type == AnnounceType.CENTER) {
|
||||
proto.setCenterSystemText(content)
|
||||
.setCenterSystemFrequency(frequency)
|
||||
;
|
||||
}else{
|
||||
}else {
|
||||
proto.setCountDownText(content)
|
||||
.setCountDownFrequency(frequency)
|
||||
;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,140 +16,140 @@ import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
|
||||
|
||||
public class MultiplayerSystem extends BaseGameSystem {
|
||||
|
||||
public MultiplayerSystem(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
public MultiplayerSystem(GameServer server) {
|
||||
super(server);
|
||||
}
|
||||
|
||||
public void applyEnterMp(Player player, int targetUid) {
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity checks - Dont let player join if already in multiplayer
|
||||
if (player.getWorld().isMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
if (target.getWorld().isDungeon()) {
|
||||
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// Get request
|
||||
CoopRequest request = target.getCoopRequests().get(player.getUid());
|
||||
|
||||
if (request != null && !request.isExpired()) {
|
||||
// Join request already exists
|
||||
return;
|
||||
}
|
||||
|
||||
// Put request in
|
||||
request = new CoopRequest(player);
|
||||
target.getCoopRequests().put(player.getUid(), request);
|
||||
|
||||
// Packet
|
||||
target.sendPacket(new PacketPlayerApplyEnterMpNotify(player));
|
||||
}
|
||||
public void applyEnterMp(Player player, int targetUid) {
|
||||
Player target = getServer().getPlayerByUid(targetUid);
|
||||
if (target == null) {
|
||||
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
|
||||
return;
|
||||
}
|
||||
|
||||
public void applyEnterMpReply(Player hostPlayer, int applyUid, boolean isAgreed) {
|
||||
// Checks
|
||||
CoopRequest request = hostPlayer.getCoopRequests().get(applyUid);
|
||||
if (request == null || request.isExpired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove now that we are handling it
|
||||
Player requester = request.getRequester();
|
||||
hostPlayer.getCoopRequests().remove(applyUid);
|
||||
|
||||
// Sanity checks - Dont let the requesting player join if they are already in multiplayer
|
||||
if (requester.getWorld().isMultiplayer()) {
|
||||
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
|
||||
return;
|
||||
}
|
||||
|
||||
// Response packet
|
||||
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE));
|
||||
|
||||
// Declined
|
||||
if (!isAgreed) {
|
||||
return;
|
||||
}
|
||||
// Sanity checks - Dont let player join if already in multiplayer
|
||||
if (player.getWorld().isMultiplayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
if (!hostPlayer.getWorld().isMultiplayer()) {
|
||||
// Player not in multiplayer, create multiplayer world
|
||||
World world = new World(hostPlayer, true);
|
||||
/*
|
||||
if (target.getWorld().isDungeon()) {
|
||||
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// Add
|
||||
world.addPlayer(hostPlayer);
|
||||
|
||||
// Rejoin packet
|
||||
hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
|
||||
}
|
||||
|
||||
// Set scene pos and id of requester to the host player's
|
||||
requester.getPosition().set(hostPlayer.getPosition());
|
||||
requester.getRotation().set(hostPlayer.getRotation());
|
||||
requester.setSceneId(hostPlayer.getSceneId());
|
||||
|
||||
// Make requester join
|
||||
hostPlayer.getWorld().addPlayer(requester);
|
||||
// Get request
|
||||
CoopRequest request = target.getCoopRequests().get(player.getUid());
|
||||
|
||||
// Packet
|
||||
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
|
||||
}
|
||||
|
||||
public boolean leaveCoop(Player player) {
|
||||
// Make sure player's world is multiplayer
|
||||
if (!player.getWorld().isMultiplayer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure everyone's scene is loaded
|
||||
for (Player p : player.getWorld().getPlayers()) {
|
||||
if (p.getSceneLoadState() != SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new world for player
|
||||
World world = new World(player);
|
||||
world.addPlayer(player);
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPosition()));
|
||||
|
||||
return true;
|
||||
}
|
||||
if (request != null && !request.isExpired()) {
|
||||
// Join request already exists
|
||||
return;
|
||||
}
|
||||
|
||||
public boolean kickPlayer(Player player, int targetUid) {
|
||||
// Make sure player's world is multiplayer and that player is owner
|
||||
if (!player.getWorld().isMultiplayer() || player.getWorld().getHost() != player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get victim and sanity checks
|
||||
Player victim = player.getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (victim == null || victim == player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure victim's scene has loaded
|
||||
if (victim.getSceneLoadState() != SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kick
|
||||
World world = new World(victim);
|
||||
world.addPlayer(victim);
|
||||
|
||||
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPosition()));
|
||||
return true;
|
||||
}
|
||||
// Put request in
|
||||
request = new CoopRequest(player);
|
||||
target.getCoopRequests().put(player.getUid(), request);
|
||||
|
||||
// Packet
|
||||
target.sendPacket(new PacketPlayerApplyEnterMpNotify(player));
|
||||
}
|
||||
|
||||
public void applyEnterMpReply(Player hostPlayer, int applyUid, boolean isAgreed) {
|
||||
// Checks
|
||||
CoopRequest request = hostPlayer.getCoopRequests().get(applyUid);
|
||||
if (request == null || request.isExpired()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove now that we are handling it
|
||||
Player requester = request.getRequester();
|
||||
hostPlayer.getCoopRequests().remove(applyUid);
|
||||
|
||||
// Sanity checks - Dont let the requesting player join if they are already in multiplayer
|
||||
if (requester.getWorld().isMultiplayer()) {
|
||||
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
|
||||
return;
|
||||
}
|
||||
|
||||
// Response packet
|
||||
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE));
|
||||
|
||||
// Declined
|
||||
if (!isAgreed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Success
|
||||
if (!hostPlayer.getWorld().isMultiplayer()) {
|
||||
// Player not in multiplayer, create multiplayer world
|
||||
World world = new World(hostPlayer, true);
|
||||
|
||||
// Add
|
||||
world.addPlayer(hostPlayer);
|
||||
|
||||
// Rejoin packet
|
||||
hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
|
||||
}
|
||||
|
||||
// Set scene pos and id of requester to the host player's
|
||||
requester.getPosition().set(hostPlayer.getPosition());
|
||||
requester.getRotation().set(hostPlayer.getRotation());
|
||||
requester.setSceneId(hostPlayer.getSceneId());
|
||||
|
||||
// Make requester join
|
||||
hostPlayer.getWorld().addPlayer(requester);
|
||||
|
||||
// Packet
|
||||
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPosition()));
|
||||
}
|
||||
|
||||
public boolean leaveCoop(Player player) {
|
||||
// Make sure player's world is multiplayer
|
||||
if (!player.getWorld().isMultiplayer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure everyone's scene is loaded
|
||||
for (Player p : player.getWorld().getPlayers()) {
|
||||
if (p.getSceneLoadState() != SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new world for player
|
||||
World world = new World(player);
|
||||
world.addPlayer(player);
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPosition()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean kickPlayer(Player player, int targetUid) {
|
||||
// Make sure player's world is multiplayer and that player is owner
|
||||
if (!player.getWorld().isMultiplayer() || player.getWorld().getHost() != player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get victim and sanity checks
|
||||
Player victim = player.getServer().getPlayerByUid(targetUid);
|
||||
|
||||
if (victim == null || victim == player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure victim's scene has loaded
|
||||
if (victim.getSceneLoadState() != SceneLoadState.LOADED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Kick
|
||||
World world = new World(victim);
|
||||
world.addPlayer(victim);
|
||||
|
||||
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPosition()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TowerManager extends BasePlayerManager {
|
||||
|
||||
|
||||
public TowerManager(Player player) {
|
||||
super(player);
|
||||
}
|
||||
|
||||
|
||||
public TowerData getTowerData() {
|
||||
return getPlayer().getTowerData();
|
||||
}
|
||||
@ -26,21 +26,21 @@ public class TowerManager extends BasePlayerManager {
|
||||
return getTowerData().currentFloorId;
|
||||
}
|
||||
|
||||
public int getCurrentLevelId(){
|
||||
public int getCurrentLevelId() {
|
||||
return getTowerData().currentLevelId + getTowerData().currentLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* form 1-3
|
||||
*/
|
||||
public int getCurrentLevel(){
|
||||
public int getCurrentLevel() {
|
||||
return getTowerData().currentLevel + 1;
|
||||
}
|
||||
private static final List<DungeonSettleListener> towerDungeonSettleListener = List.of(new TowerDungeonSettleListener());
|
||||
|
||||
|
||||
public Map<Integer, TowerLevelRecord> getRecordMap() {
|
||||
Map<Integer, TowerLevelRecord> recordMap = getTowerData().recordMap;
|
||||
if(recordMap == null || recordMap.size()==0){
|
||||
if (recordMap == null || recordMap.size()==0) {
|
||||
recordMap = new HashMap<>();
|
||||
recordMap.put(1001, new TowerLevelRecord(1001));
|
||||
getTowerData().recordMap = recordMap;
|
||||
@ -58,7 +58,7 @@ public class TowerManager extends BasePlayerManager {
|
||||
.map(TowerLevelData::getId)
|
||||
.orElse(0);
|
||||
|
||||
if (getTowerData().entryScene == 0){
|
||||
if (getTowerData().entryScene == 0) {
|
||||
getTowerData().entryScene = player.getSceneId();
|
||||
}
|
||||
|
||||
@ -88,37 +88,37 @@ public class TowerManager extends BasePlayerManager {
|
||||
player.getSession().send(new PacketTowerLevelStarCondNotify(getTowerData().currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
|
||||
public void notifyCurLevelRecordChange(){
|
||||
public void notifyCurLevelRecordChange() {
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getTowerData().currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
public void notifyCurLevelRecordChangeWhenDone(int stars){
|
||||
public void notifyCurLevelRecordChangeWhenDone(int stars) {
|
||||
Map<Integer, TowerLevelRecord> recordMap = getRecordMap();
|
||||
int currentFloorId = getTowerData().currentFloorId;
|
||||
if(!recordMap.containsKey(currentFloorId)){
|
||||
if (!recordMap.containsKey(currentFloorId)) {
|
||||
recordMap.put(currentFloorId,
|
||||
new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
|
||||
}else{
|
||||
}else {
|
||||
recordMap.put(currentFloorId,
|
||||
recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(),stars));
|
||||
}
|
||||
|
||||
getTowerData().currentLevel++;
|
||||
|
||||
if(!hasNextLevel()){
|
||||
if (!hasNextLevel()) {
|
||||
// set up the next floor
|
||||
recordMap.putIfAbsent(getNextFloorId(), new TowerLevelRecord(getNextFloorId()));
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getNextFloorId(), 1));
|
||||
}else{
|
||||
}else {
|
||||
player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel()));
|
||||
}
|
||||
}
|
||||
public boolean hasNextLevel(){
|
||||
public boolean hasNextLevel() {
|
||||
return getTowerData().currentLevel < 3;
|
||||
}
|
||||
public int getNextFloorId() {
|
||||
return player.getServer().getTowerSystem().getNextFloorId(getTowerData().currentFloorId);
|
||||
}
|
||||
public boolean hasNextFloor(){
|
||||
public boolean hasNextFloor() {
|
||||
return player.getServer().getTowerSystem().getNextFloorId(getTowerData().currentFloorId) > 0;
|
||||
}
|
||||
|
||||
@ -126,9 +126,9 @@ public class TowerManager extends BasePlayerManager {
|
||||
getTowerData().entryScene = 0;
|
||||
}
|
||||
|
||||
public boolean canEnterScheduleFloor(){
|
||||
public boolean canEnterScheduleFloor() {
|
||||
Map<Integer, TowerLevelRecord> recordMap = getRecordMap();
|
||||
if(!recordMap.containsKey(player.getServer().getTowerSystem().getLastEntranceFloor())){
|
||||
if (!recordMap.containsKey(player.getServer().getTowerSystem().getLastEntranceFloor())) {
|
||||
return false;
|
||||
}
|
||||
return recordMap.get(player.getServer().getTowerSystem().getLastEntranceFloor())
|
||||
|
@ -16,7 +16,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TowerSystem extends BaseGameSystem {
|
||||
|
||||
|
||||
public TowerSystem(GameServer server) {
|
||||
super(server);
|
||||
this.load();
|
||||
@ -24,7 +24,7 @@ public class TowerSystem extends BaseGameSystem {
|
||||
|
||||
private TowerScheduleConfig towerScheduleConfig;
|
||||
|
||||
public synchronized void load(){
|
||||
public synchronized void load() {
|
||||
try (Reader fileReader = DataLoader.loadReader("TowerSchedule.json")) {
|
||||
towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class);
|
||||
} catch (Exception e) {
|
||||
@ -36,13 +36,13 @@ public class TowerSystem extends BaseGameSystem {
|
||||
return towerScheduleConfig;
|
||||
}
|
||||
|
||||
public TowerScheduleData getCurrentTowerScheduleData(){
|
||||
public TowerScheduleData getCurrentTowerScheduleData() {
|
||||
var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId());
|
||||
if(data == null){
|
||||
if (data == null) {
|
||||
Grasscutter.getLogger().error("Could not get current tower schedule data by schedule id {}, please check your resource files",
|
||||
towerScheduleConfig.getScheduleId());
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -56,29 +56,29 @@ public class TowerSystem extends BaseGameSystem {
|
||||
return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList();
|
||||
}
|
||||
|
||||
public int getNextFloorId(int floorId){
|
||||
public int getNextFloorId(int floorId) {
|
||||
var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId();
|
||||
var scheduleFloors = getScheduleFloors();
|
||||
var nextId = 0;
|
||||
|
||||
|
||||
// find in entrance floors first
|
||||
for(int i=0;i<entranceFloors.size()-1;i++){
|
||||
if(floorId == entranceFloors.get(i)){
|
||||
for (int i=0;i<entranceFloors.size()-1;i++) {
|
||||
if (floorId == entranceFloors.get(i)) {
|
||||
nextId = entranceFloors.get(i+1);
|
||||
}
|
||||
}
|
||||
|
||||
if(floorId == entranceFloors.get(entranceFloors.size()-1)){
|
||||
|
||||
if (floorId == entranceFloors.get(entranceFloors.size()-1)) {
|
||||
nextId = scheduleFloors.get(0);
|
||||
}
|
||||
|
||||
if(nextId != 0){
|
||||
|
||||
if (nextId != 0) {
|
||||
return nextId;
|
||||
}
|
||||
|
||||
|
||||
// find in schedule floors
|
||||
for(int i=0; i < scheduleFloors.size() - 1; i++){
|
||||
if(floorId == scheduleFloors.get(i)){
|
||||
for (int i=0; i < scheduleFloors.size() - 1; i++) {
|
||||
if (floorId == scheduleFloors.get(i)) {
|
||||
nextId = scheduleFloors.get(i + 1);
|
||||
}
|
||||
}return nextId;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,62 +7,62 @@ import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.utils.Position;
|
||||
|
||||
public class SpawnDataEntry {
|
||||
private transient SpawnGroupEntry group;
|
||||
private int monsterId;
|
||||
private int gadgetId;
|
||||
private int configId;
|
||||
private int level;
|
||||
private int poseId;
|
||||
private int gatherItemId;
|
||||
private transient SpawnGroupEntry group;
|
||||
private int monsterId;
|
||||
private int gadgetId;
|
||||
private int configId;
|
||||
private int level;
|
||||
private int poseId;
|
||||
private int gatherItemId;
|
||||
private int gadgetState;
|
||||
private Position pos;
|
||||
private Position rot;
|
||||
private Position pos;
|
||||
private Position rot;
|
||||
|
||||
public SpawnGroupEntry getGroup() {
|
||||
return group;
|
||||
}
|
||||
public SpawnGroupEntry getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(SpawnGroupEntry group) {
|
||||
this.group = group;
|
||||
}
|
||||
public void setGroup(SpawnGroupEntry group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public int getMonsterId() {
|
||||
return monsterId;
|
||||
}
|
||||
public int getMonsterId() {
|
||||
return monsterId;
|
||||
}
|
||||
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
public int getGadgetId() {
|
||||
return gadgetId;
|
||||
}
|
||||
|
||||
public int getGadgetState() {
|
||||
return gadgetState;
|
||||
}
|
||||
|
||||
public int getConfigId() {
|
||||
return configId;
|
||||
}
|
||||
return configId;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getPoseId() {
|
||||
return poseId;
|
||||
}
|
||||
public int getPoseId() {
|
||||
return poseId;
|
||||
}
|
||||
|
||||
public int getGatherItemId() {
|
||||
return gatherItemId;
|
||||
}
|
||||
public int getGatherItemId() {
|
||||
return gatherItemId;
|
||||
}
|
||||
|
||||
public Position getPos() {
|
||||
return pos;
|
||||
}
|
||||
public Position getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public Position getRot() {
|
||||
return rot;
|
||||
}
|
||||
public Position getRot() {
|
||||
return rot;
|
||||
}
|
||||
|
||||
public GridBlockId getBlockId(){
|
||||
public GridBlockId getBlockId() {
|
||||
int scale = GridBlockId.getScale(gadgetId);
|
||||
return new GridBlockId(group.sceneId,scale,
|
||||
(int)(pos.getX() / GameDepot.BLOCK_SIZE[scale]),
|
||||
@ -70,32 +70,32 @@ public class SpawnDataEntry {
|
||||
);
|
||||
}
|
||||
|
||||
public static class SpawnGroupEntry {
|
||||
private int sceneId;
|
||||
private int groupId;
|
||||
private int blockId;
|
||||
private List<SpawnDataEntry> spawns;
|
||||
public static class SpawnGroupEntry {
|
||||
private int sceneId;
|
||||
private int groupId;
|
||||
private int blockId;
|
||||
private List<SpawnDataEntry> spawns;
|
||||
|
||||
public int getSceneId() {
|
||||
return sceneId;
|
||||
}
|
||||
public int getSceneId() {
|
||||
return sceneId;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
public int getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public int getBlockId() {
|
||||
return blockId;
|
||||
}
|
||||
public int getBlockId() {
|
||||
return blockId;
|
||||
}
|
||||
|
||||
public void setBlockId(int blockId) {
|
||||
this.blockId = blockId;
|
||||
}
|
||||
public void setBlockId(int blockId) {
|
||||
this.blockId = blockId;
|
||||
}
|
||||
|
||||
public List<SpawnDataEntry> getSpawns() {
|
||||
return spawns;
|
||||
}
|
||||
}
|
||||
public List<SpawnDataEntry> getSpawns() {
|
||||
return spawns;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GridBlockId {
|
||||
int sceneId;
|
||||
@ -133,7 +133,7 @@ public class SpawnDataEntry {
|
||||
return Objects.hash(sceneId, scale, x, z);
|
||||
}
|
||||
|
||||
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos){
|
||||
public static GridBlockId[] getAdjacentGridBlockIds(int sceneId, Position pos) {
|
||||
GridBlockId[] results = new GridBlockId[5*5*GameDepot.BLOCK_SIZE.length];
|
||||
int t=0;
|
||||
for (int scale = 0; scale < GameDepot.BLOCK_SIZE.length; scale++) {
|
||||
@ -148,7 +148,7 @@ public class SpawnDataEntry {
|
||||
return results;
|
||||
}
|
||||
|
||||
public static int getScale(int gadgetId){
|
||||
public static int getScale(int gadgetId) {
|
||||
return 0;//you should implement here,this is index of GameDepot.BLOCK_SIZE
|
||||
}
|
||||
}
|
||||
|
@ -31,298 +31,298 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class World implements Iterable<Player> {
|
||||
private final GameServer server;
|
||||
private final Player owner;
|
||||
private final List<Player> players;
|
||||
private final Int2ObjectMap<Scene> scenes;
|
||||
|
||||
private int levelEntityId;
|
||||
private int nextEntityId = 0;
|
||||
private int nextPeerId = 0;
|
||||
private int worldLevel;
|
||||
private final GameServer server;
|
||||
private final Player owner;
|
||||
private final List<Player> players;
|
||||
private final Int2ObjectMap<Scene> scenes;
|
||||
|
||||
private boolean isMultiplayer;
|
||||
|
||||
public World(Player player) {
|
||||
this(player, false);
|
||||
}
|
||||
|
||||
public World(Player player, boolean isMultiplayer) {
|
||||
this.owner = player;
|
||||
this.server = player.getServer();
|
||||
this.players = Collections.synchronizedList(new ArrayList<>());
|
||||
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
|
||||
|
||||
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
|
||||
this.worldLevel = player.getWorldLevel();
|
||||
this.isMultiplayer = isMultiplayer;
|
||||
private int levelEntityId;
|
||||
private int nextEntityId = 0;
|
||||
private int nextPeerId = 0;
|
||||
private int worldLevel;
|
||||
|
||||
this.owner.getServer().registerWorld(this);
|
||||
}
|
||||
|
||||
public Player getHost() {
|
||||
return owner;
|
||||
}
|
||||
private boolean isMultiplayer;
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
public World(Player player) {
|
||||
this(player, false);
|
||||
}
|
||||
|
||||
public int getLevelEntityId() {
|
||||
return levelEntityId;
|
||||
}
|
||||
public World(Player player, boolean isMultiplayer) {
|
||||
this.owner = player;
|
||||
this.server = player.getServer();
|
||||
this.players = Collections.synchronizedList(new ArrayList<>());
|
||||
this.scenes = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
|
||||
|
||||
public int getHostPeerId() {
|
||||
if (this.getHost() == null) {
|
||||
return 0;
|
||||
}
|
||||
return this.getHost().getPeerId();
|
||||
}
|
||||
|
||||
public int getNextPeerId() {
|
||||
return ++this.nextPeerId;
|
||||
}
|
||||
this.levelEntityId = getNextEntityId(EntityIdType.MPLEVEL);
|
||||
this.worldLevel = player.getWorldLevel();
|
||||
this.isMultiplayer = isMultiplayer;
|
||||
|
||||
public int getWorldLevel() {
|
||||
return worldLevel;
|
||||
}
|
||||
this.owner.getServer().registerWorld(this);
|
||||
}
|
||||
|
||||
public void setWorldLevel(int worldLevel) {
|
||||
this.worldLevel = worldLevel;
|
||||
}
|
||||
public Player getHost() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public List<Player> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
public Int2ObjectMap<Scene> getScenes() {
|
||||
return this.scenes;
|
||||
}
|
||||
|
||||
public Scene getSceneById(int sceneId) {
|
||||
// Get scene normally
|
||||
Scene scene = getScenes().get(sceneId);
|
||||
if (scene != null) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
// Create scene from scene data if it doesnt exist
|
||||
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
|
||||
if (sceneData != null) {
|
||||
scene = new Scene(this, sceneData);
|
||||
this.registerScene(scene);
|
||||
return scene;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getPlayerCount() {
|
||||
return getPlayers().size();
|
||||
}
|
||||
|
||||
public boolean isMultiplayer() {
|
||||
return isMultiplayer;
|
||||
}
|
||||
|
||||
public int getNextEntityId(EntityIdType idType) {
|
||||
return (idType.getId() << 24) + ++this.nextEntityId;
|
||||
}
|
||||
|
||||
public synchronized void addPlayer(Player player) {
|
||||
// Check if player already in
|
||||
if (getPlayers().contains(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove player from prev world
|
||||
if (player.getWorld() != null) {
|
||||
player.getWorld().removePlayer(player);
|
||||
}
|
||||
|
||||
// Register
|
||||
player.setWorld(this);
|
||||
getPlayers().add(player);
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
// Set player variables
|
||||
player.setPeerId(this.getNextPeerId());
|
||||
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
|
||||
|
||||
// Copy main team to mp team
|
||||
if (this.isMultiplayer()) {
|
||||
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().setCurrentCharacterIndex(0);
|
||||
}
|
||||
|
||||
// Add to scene
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.addPlayer(player);
|
||||
public int getLevelEntityId() {
|
||||
return levelEntityId;
|
||||
}
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 1) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
}
|
||||
public int getHostPeerId() {
|
||||
if (this.getHost() == null) {
|
||||
return 0;
|
||||
}
|
||||
return this.getHost().getPeerId();
|
||||
}
|
||||
|
||||
public synchronized void removePlayer(Player player) {
|
||||
// Remove team entities
|
||||
player.sendPacket(
|
||||
new PacketDelTeamEntityNotify(
|
||||
player.getSceneId(),
|
||||
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
|
||||
)
|
||||
);
|
||||
|
||||
// Deregister
|
||||
getPlayers().remove(player);
|
||||
player.setWorld(null);
|
||||
|
||||
// Remove from scene
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.removePlayer(player);
|
||||
public int getNextPeerId() {
|
||||
return ++this.nextPeerId;
|
||||
}
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 0) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
public int getWorldLevel() {
|
||||
return worldLevel;
|
||||
}
|
||||
|
||||
// Disband world if host leaves
|
||||
if (getHost() == player) {
|
||||
List<Player> kicked = new ArrayList<>(this.getPlayers());
|
||||
for (Player victim : kicked) {
|
||||
World world = new World(victim);
|
||||
world.addPlayer(victim);
|
||||
|
||||
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerScene(Scene scene) {
|
||||
this.getScenes().put(scene.getId(), scene);
|
||||
}
|
||||
|
||||
public void deregisterScene(Scene scene) {
|
||||
this.getScenes().remove(scene.getId());
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
|
||||
return transferPlayerToScene(player, sceneId, null, pos);
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
|
||||
return transferPlayerToScene(player, sceneId, data, null);
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
|
||||
if (GameData.getSceneDataMap().get(sceneId) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Scene oldScene = null;
|
||||
public void setWorldLevel(int worldLevel) {
|
||||
this.worldLevel = worldLevel;
|
||||
}
|
||||
|
||||
if (player.getScene() != null) {
|
||||
oldScene = player.getScene();
|
||||
public List<Player> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
// Dont deregister scenes if the player is going to tp back into them
|
||||
if (oldScene.getId() == sceneId) {
|
||||
oldScene.setDontDestroyWhenEmpty(true);
|
||||
}
|
||||
public Int2ObjectMap<Scene> getScenes() {
|
||||
return this.scenes;
|
||||
}
|
||||
|
||||
oldScene.removePlayer(player);
|
||||
}
|
||||
|
||||
Scene newScene = this.getSceneById(sceneId);
|
||||
newScene.setDungeonData(dungeonData);
|
||||
newScene.addPlayer(player);
|
||||
|
||||
// Dungeon
|
||||
SceneConfig config = newScene.getScriptManager().getConfig();
|
||||
if (pos == null && config != null) {
|
||||
if (config.born_pos != null) {
|
||||
pos = newScene.getScriptManager().getConfig().born_pos;
|
||||
}
|
||||
if (config.born_rot != null) {
|
||||
player.getRotation().set(config.born_rot);
|
||||
}
|
||||
}
|
||||
|
||||
// Set player position
|
||||
if (pos == null) {
|
||||
pos = player.getPosition();
|
||||
}
|
||||
|
||||
player.getPosition().set(pos);
|
||||
public Scene getSceneById(int sceneId) {
|
||||
// Get scene normally
|
||||
Scene scene = getScenes().get(sceneId);
|
||||
if (scene != null) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
if (oldScene != null) {
|
||||
newScene.setPrevScene(oldScene.getId());
|
||||
oldScene.setDontDestroyWhenEmpty(false);
|
||||
}
|
||||
// Create scene from scene data if it doesnt exist
|
||||
SceneData sceneData = GameData.getSceneDataMap().get(sceneId);
|
||||
if (sceneData != null) {
|
||||
scene = new Scene(this, sceneData);
|
||||
this.registerScene(scene);
|
||||
return scene;
|
||||
}
|
||||
|
||||
// Get enter types
|
||||
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
|
||||
EnterReason enterReason = EnterReason.TransPoint;
|
||||
|
||||
if (dungeonData != null) {
|
||||
enterType = EnterType.ENTER_TYPE_DUNGEON;
|
||||
enterReason = EnterReason.DungeonEnter;
|
||||
} else if (oldScene == newScene) {
|
||||
enterType = EnterType.ENTER_TYPE_GOTO;
|
||||
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
|
||||
// Home
|
||||
enterReason = EnterReason.EnterHome;
|
||||
enterType = EnterType.ENTER_TYPE_SELF_HOME;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Teleport packet
|
||||
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updatePlayerInfos(Player paramPlayer) {
|
||||
for (Player player : getPlayers()) {
|
||||
// Dont send packets if player is loading in and filter out joining player
|
||||
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update team of all players since max players has been changed - Probably not the best way to do it
|
||||
if (this.isMultiplayer()) {
|
||||
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().updateTeamEntities(null);
|
||||
}
|
||||
public int getPlayerCount() {
|
||||
return getPlayers().size();
|
||||
}
|
||||
|
||||
// World player info packets
|
||||
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
|
||||
player.getSession().send(new PacketScenePlayerInfoNotify(this));
|
||||
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
|
||||
|
||||
// Team packets
|
||||
player.getSession().send(new PacketSyncTeamEntityNotify(player));
|
||||
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastPacket(BasePacket packet) {
|
||||
// Send to all players - might have to check if player has been sent data packets
|
||||
for (Player player : this.getPlayers()) {
|
||||
player.getSession().send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void onTick() {
|
||||
for (Scene scene : this.getScenes().values()) {
|
||||
scene.onTick();
|
||||
}
|
||||
}
|
||||
public boolean isMultiplayer() {
|
||||
return isMultiplayer;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
}
|
||||
public int getNextEntityId(EntityIdType idType) {
|
||||
return (idType.getId() << 24) + ++this.nextEntityId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Player> iterator() {
|
||||
return getPlayers().iterator();
|
||||
}
|
||||
public synchronized void addPlayer(Player player) {
|
||||
// Check if player already in
|
||||
if (getPlayers().contains(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove player from prev world
|
||||
if (player.getWorld() != null) {
|
||||
player.getWorld().removePlayer(player);
|
||||
}
|
||||
|
||||
// Register
|
||||
player.setWorld(this);
|
||||
getPlayers().add(player);
|
||||
|
||||
// Set player variables
|
||||
player.setPeerId(this.getNextPeerId());
|
||||
player.getTeamManager().setEntityId(getNextEntityId(EntityIdType.TEAM));
|
||||
|
||||
// Copy main team to mp team
|
||||
if (this.isMultiplayer()) {
|
||||
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getCurrentSinglePlayerTeamInfo(), player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().setCurrentCharacterIndex(0);
|
||||
}
|
||||
|
||||
// Add to scene
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.addPlayer(player);
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 1) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removePlayer(Player player) {
|
||||
// Remove team entities
|
||||
player.sendPacket(
|
||||
new PacketDelTeamEntityNotify(
|
||||
player.getSceneId(),
|
||||
getPlayers().stream().map(p -> p.getTeamManager().getEntityId()).collect(Collectors.toList())
|
||||
)
|
||||
);
|
||||
|
||||
// Deregister
|
||||
getPlayers().remove(player);
|
||||
player.setWorld(null);
|
||||
|
||||
// Remove from scene
|
||||
Scene scene = this.getSceneById(player.getSceneId());
|
||||
scene.removePlayer(player);
|
||||
|
||||
// Info packet for other players
|
||||
if (this.getPlayers().size() > 0) {
|
||||
this.updatePlayerInfos(player);
|
||||
}
|
||||
|
||||
// Disband world if host leaves
|
||||
if (getHost() == player) {
|
||||
List<Player> kicked = new ArrayList<>(this.getPlayers());
|
||||
for (Player victim : kicked) {
|
||||
World world = new World(victim);
|
||||
world.addPlayer(victim);
|
||||
|
||||
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPosition()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerScene(Scene scene) {
|
||||
this.getScenes().put(scene.getId(), scene);
|
||||
}
|
||||
|
||||
public void deregisterScene(Scene scene) {
|
||||
this.getScenes().remove(scene.getId());
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, Position pos) {
|
||||
return transferPlayerToScene(player, sceneId, null, pos);
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData data) {
|
||||
return transferPlayerToScene(player, sceneId, data, null);
|
||||
}
|
||||
|
||||
public boolean transferPlayerToScene(Player player, int sceneId, DungeonData dungeonData, Position pos) {
|
||||
if (GameData.getSceneDataMap().get(sceneId) == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Scene oldScene = null;
|
||||
|
||||
if (player.getScene() != null) {
|
||||
oldScene = player.getScene();
|
||||
|
||||
// Dont deregister scenes if the player is going to tp back into them
|
||||
if (oldScene.getId() == sceneId) {
|
||||
oldScene.setDontDestroyWhenEmpty(true);
|
||||
}
|
||||
|
||||
oldScene.removePlayer(player);
|
||||
}
|
||||
|
||||
Scene newScene = this.getSceneById(sceneId);
|
||||
newScene.setDungeonData(dungeonData);
|
||||
newScene.addPlayer(player);
|
||||
|
||||
// Dungeon
|
||||
SceneConfig config = newScene.getScriptManager().getConfig();
|
||||
if (pos == null && config != null) {
|
||||
if (config.born_pos != null) {
|
||||
pos = newScene.getScriptManager().getConfig().born_pos;
|
||||
}
|
||||
if (config.born_rot != null) {
|
||||
player.getRotation().set(config.born_rot);
|
||||
}
|
||||
}
|
||||
|
||||
// Set player position
|
||||
if (pos == null) {
|
||||
pos = player.getPosition();
|
||||
}
|
||||
|
||||
player.getPosition().set(pos);
|
||||
|
||||
if (oldScene != null) {
|
||||
newScene.setPrevScene(oldScene.getId());
|
||||
oldScene.setDontDestroyWhenEmpty(false);
|
||||
}
|
||||
|
||||
// Get enter types
|
||||
EnterType enterType = EnterType.ENTER_TYPE_JUMP;
|
||||
EnterReason enterReason = EnterReason.TransPoint;
|
||||
|
||||
if (dungeonData != null) {
|
||||
enterType = EnterType.ENTER_TYPE_DUNGEON;
|
||||
enterReason = EnterReason.DungeonEnter;
|
||||
} else if (oldScene == newScene) {
|
||||
enterType = EnterType.ENTER_TYPE_GOTO;
|
||||
} else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) {
|
||||
// Home
|
||||
enterReason = EnterReason.EnterHome;
|
||||
enterType = EnterType.ENTER_TYPE_SELF_HOME;
|
||||
|
||||
}
|
||||
|
||||
// Teleport packet
|
||||
player.sendPacket(new PacketPlayerEnterSceneNotify(player, enterType, enterReason, sceneId, pos));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updatePlayerInfos(Player paramPlayer) {
|
||||
for (Player player : getPlayers()) {
|
||||
// Dont send packets if player is loading in and filter out joining player
|
||||
if (!player.hasSentAvatarDataNotify() || player.getSceneLoadState().getValue() < SceneLoadState.INIT.getValue() || player == paramPlayer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update team of all players since max players has been changed - Probably not the best way to do it
|
||||
if (this.isMultiplayer()) {
|
||||
player.getTeamManager().getMpTeam().copyFrom(player.getTeamManager().getMpTeam(), player.getTeamManager().getMaxTeamSize());
|
||||
player.getTeamManager().updateTeamEntities(null);
|
||||
}
|
||||
|
||||
// World player info packets
|
||||
player.getSession().send(new PacketWorldPlayerInfoNotify(this));
|
||||
player.getSession().send(new PacketScenePlayerInfoNotify(this));
|
||||
player.getSession().send(new PacketWorldPlayerRTTNotify(this));
|
||||
|
||||
// Team packets
|
||||
player.getSession().send(new PacketSyncTeamEntityNotify(player));
|
||||
player.getSession().send(new PacketSyncScenePlayTeamEntityNotify(player));
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastPacket(BasePacket packet) {
|
||||
// Send to all players - might have to check if player has been sent data packets
|
||||
for (Player player : this.getPlayers()) {
|
||||
player.getSession().send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void onTick() {
|
||||
for (Scene scene : this.getScenes().values()) {
|
||||
scene.onTick();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Player> iterator() {
|
||||
return getPlayers().iterator();
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
|
||||
|
||||
public WorldDataSystem(GameServer server){
|
||||
public WorldDataSystem(GameServer server) {
|
||||
super(server);
|
||||
this.chestInteractHandlerMap = new HashMap<>();
|
||||
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
|
||||
@ -38,15 +38,15 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
loadChestConfig();
|
||||
}
|
||||
|
||||
public synchronized void loadChestConfig(){
|
||||
public synchronized void loadChestConfig() {
|
||||
// set the special chest first
|
||||
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||
|
||||
try(Reader reader = DataLoader.loadReader("ChestReward.json")) {
|
||||
try (Reader reader = DataLoader.loadReader("ChestReward.json")) {
|
||||
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
|
||||
reader,
|
||||
reader,
|
||||
TypeToken.getParameterized(List.class, ChestReward.class).getType());
|
||||
|
||||
|
||||
chestReward.forEach(reward ->
|
||||
reward.getObjNames().forEach(
|
||||
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
|
||||
@ -60,21 +60,21 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
return chestInteractHandlerMap;
|
||||
}
|
||||
|
||||
public RewardPreviewData getRewardByBossId(int monsterId){
|
||||
public RewardPreviewData getRewardByBossId(int monsterId) {
|
||||
var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||
.findFirst();
|
||||
|
||||
if(investigationMonsterData.isEmpty()){
|
||||
if (investigationMonsterData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId());
|
||||
}
|
||||
|
||||
private SceneGroup getInvestigationGroup(int sceneId, int groupId){
|
||||
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
|
||||
var key = sceneId + "_" + groupId;
|
||||
if(!sceneInvestigationGroupMap.containsKey(key)){
|
||||
if (!sceneInvestigationGroupMap.containsKey(key)) {
|
||||
var group = SceneGroup.of(groupId).load(sceneId);
|
||||
sceneInvestigationGroupMap.putIfAbsent(key, group);
|
||||
return group;
|
||||
@ -82,7 +82,7 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
return sceneInvestigationGroupMap.get(key);
|
||||
}
|
||||
|
||||
public int getMonsterLevel(SceneMonster monster, World world){
|
||||
public int getMonsterLevel(SceneMonster monster, World world) {
|
||||
// Calculate level
|
||||
int level = monster.level;
|
||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
|
||||
@ -98,14 +98,14 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
var sceneId = imd.getCityData().getSceneId();
|
||||
var group = getInvestigationGroup(sceneId, groupId);
|
||||
|
||||
if(group == null || group.monsters == null){
|
||||
if (group == null || group.monsters == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var monster = group.monsters.values().stream()
|
||||
.filter(x -> x.monster_id == monsterId)
|
||||
.findFirst();
|
||||
if(monster.isEmpty()){
|
||||
if (monster.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -122,9 +122,9 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
.setRefreshInterval(Integer.MAX_VALUE)
|
||||
.setPos(monster.get().pos.toProto());
|
||||
|
||||
if("Boss".equals(imd.getMonsterCategory())){
|
||||
if ("Boss".equals(imd.getMonsterCategory())) {
|
||||
var bossChest = group.searchBossChestInGroup();
|
||||
if(bossChest.isPresent()){
|
||||
if (bossChest.isPresent()) {
|
||||
builder.setResin(bossChest.get().resin);
|
||||
builder.setBossChestNum(bossChest.get().take_num);
|
||||
}
|
||||
@ -132,9 +132,9 @@ public class WorldDataSystem extends BaseGameSystem {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId){
|
||||
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(Player player, int cityId) {
|
||||
var cityData = GameData.getCityDataMap().get(cityId);
|
||||
if(cityData == null){
|
||||
if (cityData == null) {
|
||||
Grasscutter.getLogger().warn("City not exist {}", cityId);
|
||||
return List.of();
|
||||
}
|
||||
|
@ -8,134 +8,134 @@ import emu.grasscutter.net.proto.PacketHeadOuterClass.PacketHead;
|
||||
import emu.grasscutter.utils.Crypto;
|
||||
|
||||
public class BasePacket {
|
||||
private static final int const1 = 17767; // 0x4567
|
||||
private static final int const2 = -30293; // 0x89ab
|
||||
|
||||
private int opcode;
|
||||
private boolean shouldBuildHeader = false;
|
||||
private static final int const1 = 17767; // 0x4567
|
||||
private static final int const2 = -30293; // 0x89ab
|
||||
|
||||
private byte[] header;
|
||||
private byte[] data;
|
||||
|
||||
// Encryption
|
||||
private boolean useDispatchKey;
|
||||
public boolean shouldEncrypt = true;
|
||||
|
||||
public BasePacket(int opcode) {
|
||||
this.opcode = opcode;
|
||||
}
|
||||
|
||||
public BasePacket(int opcode, int clientSequence) {
|
||||
this.opcode = opcode;
|
||||
this.buildHeader(clientSequence);
|
||||
}
|
||||
|
||||
public BasePacket(int opcode, boolean buildHeader) {
|
||||
this.opcode = opcode;
|
||||
this.shouldBuildHeader = buildHeader;
|
||||
}
|
||||
private int opcode;
|
||||
private boolean shouldBuildHeader = false;
|
||||
|
||||
public int getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
private byte[] header;
|
||||
private byte[] data;
|
||||
|
||||
public void setOpcode(int opcode) {
|
||||
this.opcode = opcode;
|
||||
}
|
||||
// Encryption
|
||||
private boolean useDispatchKey;
|
||||
public boolean shouldEncrypt = true;
|
||||
|
||||
public boolean useDispatchKey() {
|
||||
return useDispatchKey;
|
||||
}
|
||||
public BasePacket(int opcode) {
|
||||
this.opcode = opcode;
|
||||
}
|
||||
|
||||
public void setUseDispatchKey(boolean useDispatchKey) {
|
||||
this.useDispatchKey = useDispatchKey;
|
||||
}
|
||||
public BasePacket(int opcode, int clientSequence) {
|
||||
this.opcode = opcode;
|
||||
this.buildHeader(clientSequence);
|
||||
}
|
||||
|
||||
public byte[] getHeader() {
|
||||
return header;
|
||||
}
|
||||
public BasePacket(int opcode, boolean buildHeader) {
|
||||
this.opcode = opcode;
|
||||
this.shouldBuildHeader = buildHeader;
|
||||
}
|
||||
|
||||
public void setHeader(byte[] header) {
|
||||
this.header = header;
|
||||
}
|
||||
public int getOpcode() {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
public boolean shouldBuildHeader() {
|
||||
return shouldBuildHeader;
|
||||
}
|
||||
public void setOpcode(int opcode) {
|
||||
this.opcode = opcode;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
public boolean useDispatchKey() {
|
||||
return useDispatchKey;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setData(GeneratedMessageV3 proto) {
|
||||
this.data = proto.toByteArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setData(GeneratedMessageV3.Builder proto) {
|
||||
this.data = proto.build().toByteArray();
|
||||
}
|
||||
|
||||
public BasePacket buildHeader(int clientSequence) {
|
||||
if (this.getHeader() != null && clientSequence == 0) {
|
||||
return this;
|
||||
}
|
||||
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] build() {
|
||||
if (getHeader() == null) {
|
||||
this.header = new byte[0];
|
||||
}
|
||||
|
||||
if (getData() == null) {
|
||||
this.data = new byte[0];
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
|
||||
|
||||
this.writeUint16(baos, const1);
|
||||
this.writeUint16(baos, opcode);
|
||||
this.writeUint16(baos, header.length);
|
||||
this.writeUint32(baos, data.length);
|
||||
this.writeBytes(baos, header);
|
||||
this.writeBytes(baos, data);
|
||||
this.writeUint16(baos, const2);
|
||||
|
||||
byte[] packet = baos.toByteArray();
|
||||
|
||||
if (this.shouldEncrypt) {
|
||||
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
|
||||
}
|
||||
public void setUseDispatchKey(boolean useDispatchKey) {
|
||||
this.useDispatchKey = useDispatchKey;
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void writeUint16(ByteArrayOutputStream baos, int i) {
|
||||
// Unsigned short
|
||||
public byte[] getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public void setHeader(byte[] header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public boolean shouldBuildHeader() {
|
||||
return shouldBuildHeader;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setData(GeneratedMessageV3 proto) {
|
||||
this.data = proto.toByteArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void setData(GeneratedMessageV3.Builder proto) {
|
||||
this.data = proto.build().toByteArray();
|
||||
}
|
||||
|
||||
public BasePacket buildHeader(int clientSequence) {
|
||||
if (this.getHeader() != null && clientSequence == 0) {
|
||||
return this;
|
||||
}
|
||||
setHeader(PacketHead.newBuilder().setClientSequenceId(clientSequence).setSentMs(System.currentTimeMillis()).build().toByteArray());
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] build() {
|
||||
if (getHeader() == null) {
|
||||
this.header = new byte[0];
|
||||
}
|
||||
|
||||
if (getData() == null) {
|
||||
this.data = new byte[0];
|
||||
}
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(2 + 2 + 2 + 4 + getHeader().length + getData().length + 2);
|
||||
|
||||
this.writeUint16(baos, const1);
|
||||
this.writeUint16(baos, opcode);
|
||||
this.writeUint16(baos, header.length);
|
||||
this.writeUint32(baos, data.length);
|
||||
this.writeBytes(baos, header);
|
||||
this.writeBytes(baos, data);
|
||||
this.writeUint16(baos, const2);
|
||||
|
||||
byte[] packet = baos.toByteArray();
|
||||
|
||||
if (this.shouldEncrypt) {
|
||||
Crypto.xor(packet, this.useDispatchKey() ? Crypto.DISPATCH_KEY : Crypto.ENCRYPT_KEY);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void writeUint16(ByteArrayOutputStream baos, int i) {
|
||||
// Unsigned short
|
||||
baos.write((byte) ((i >>> 8) & 0xFF));
|
||||
baos.write((byte) (i & 0xFF));
|
||||
}
|
||||
|
||||
public void writeUint32(ByteArrayOutputStream baos, int i) {
|
||||
// Unsigned int (long)
|
||||
|
||||
public void writeUint32(ByteArrayOutputStream baos, int i) {
|
||||
// Unsigned int (long)
|
||||
baos.write((byte) ((i >>> 24) & 0xFF));
|
||||
baos.write((byte) ((i >>> 16) & 0xFF));
|
||||
baos.write((byte) ((i >>> 8) & 0xFF));
|
||||
baos.write((byte) (i & 0xFF));
|
||||
}
|
||||
|
||||
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
|
||||
try {
|
||||
baos.write(bytes);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
public void writeBytes(ByteArrayOutputStream baos, byte[] bytes) {
|
||||
try {
|
||||
baos.write(bytes);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1854,4 +1854,4 @@ public class PacketOpcodes {
|
||||
public static final int WorldRoutineTypeCloseNotify = 3502;
|
||||
public static final int WorldRoutineTypeRefreshNotify = 3525;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ public class PacketOpcodesUtils {
|
||||
PacketOpcodes.WindSeedClientNotify,
|
||||
PacketOpcodes.PlayerLuaShellNotify
|
||||
);
|
||||
|
||||
|
||||
public static final Set<Integer> LOOP_PACKETS = Set.of(
|
||||
PacketOpcodes.PingReq,
|
||||
PacketOpcodes.PingRsp,
|
||||
@ -30,39 +30,39 @@ public class PacketOpcodesUtils {
|
||||
PacketOpcodes.UnionCmdNotify,
|
||||
PacketOpcodes.QueryPathReq
|
||||
);
|
||||
|
||||
static {
|
||||
opcodeMap = new Int2ObjectOpenHashMap<String>();
|
||||
|
||||
Field[] fields = PacketOpcodes.class.getFields();
|
||||
static {
|
||||
opcodeMap = new Int2ObjectOpenHashMap<String>();
|
||||
|
||||
for (Field f : fields) {
|
||||
if(f.getType().equals(int.class)) {
|
||||
try {
|
||||
opcodeMap.put(f.getInt(null), f.getName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Field[] fields = PacketOpcodes.class.getFields();
|
||||
|
||||
public static String getOpcodeName(int opcode) {
|
||||
if (opcode <= 0) return "UNKNOWN";
|
||||
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
|
||||
}
|
||||
for (Field f : fields) {
|
||||
if (f.getType().equals(int.class)) {
|
||||
try {
|
||||
opcodeMap.put(f.getInt(null), f.getName());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpPacketIds() {
|
||||
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
|
||||
// Create sorted tree map
|
||||
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
|
||||
.filter(e -> e.getIntKey() > 0)
|
||||
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
|
||||
// Write to file
|
||||
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
|
||||
Grasscutter.getLogger().info("Dumped packet ids.");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
public static String getOpcodeName(int opcode) {
|
||||
if (opcode <= 0) return "UNKNOWN";
|
||||
return opcodeMap.getOrDefault(opcode, "UNKNOWN");
|
||||
}
|
||||
|
||||
public static void dumpPacketIds() {
|
||||
try (FileWriter writer = new FileWriter("./PacketIds_" + GameConstants.VERSION + ".json")) {
|
||||
// Create sorted tree map
|
||||
Map<Integer, String> packetIds = opcodeMap.int2ObjectEntrySet().stream()
|
||||
.filter(e -> e.getIntKey() > 0)
|
||||
.collect(Collectors.toMap(Int2ObjectMap.Entry::getIntKey, Int2ObjectMap.Entry::getValue, (k, v) -> v, TreeMap::new));
|
||||
// Write to file
|
||||
writer.write(Grasscutter.getGsonFactory().toJson(packetIds));
|
||||
Grasscutter.getLogger().info("Dumped packet ids.");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import java.net.URLClassLoader;
|
||||
*/
|
||||
public abstract class Plugin {
|
||||
private final ServerHook server = ServerHook.getInstance();
|
||||
|
||||
|
||||
private PluginIdentifier identifier;
|
||||
private URLClassLoader classLoader;
|
||||
private File dataFolder;
|
||||
@ -25,22 +25,22 @@ public abstract class Plugin {
|
||||
|
||||
/**
|
||||
* This method is reflected into.
|
||||
*
|
||||
*
|
||||
* Set plugin variables.
|
||||
* @param identifier The plugin's identifier.
|
||||
*/
|
||||
private void initializePlugin(PluginIdentifier identifier, URLClassLoader classLoader) {
|
||||
if(this.identifier != null) {
|
||||
if (this.identifier != null) {
|
||||
Grasscutter.getLogger().warn(this.identifier.name + " had a reinitialization attempt.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.identifier = identifier;
|
||||
this.classLoader = classLoader;
|
||||
this.dataFolder = new File(PLUGIN(), identifier.name);
|
||||
this.logger = LoggerFactory.getLogger(identifier.name);
|
||||
|
||||
if(!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
|
||||
|
||||
if (!this.dataFolder.exists() && !this.dataFolder.mkdirs()) {
|
||||
Grasscutter.getLogger().warn("Failed to create plugin data folder for " + this.identifier.name);
|
||||
return;
|
||||
}
|
||||
@ -50,7 +50,7 @@ public abstract class Plugin {
|
||||
* The plugin's identifier instance.
|
||||
* @return An instance of {@link PluginIdentifier}.
|
||||
*/
|
||||
public final PluginIdentifier getIdentifier(){
|
||||
public final PluginIdentifier getIdentifier() {
|
||||
return this.identifier;
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public abstract class Plugin {
|
||||
public final Logger getLogger() {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
|
||||
/* Called when the plugin is first loaded. */
|
||||
public void onLoad() { }
|
||||
/* Called after (most of) the server enables. */
|
||||
|
@ -72,7 +72,7 @@ public final class PluginManager {
|
||||
List<PluginData> dependencies = new ArrayList<>();
|
||||
|
||||
// Initialize all plugins.
|
||||
for(var plugin : plugins) {
|
||||
for (var plugin : plugins) {
|
||||
try {
|
||||
URL url = plugin.toURI().toURL();
|
||||
try (URLClassLoader loader = new URLClassLoader(new URL[]{url})) {
|
||||
@ -109,7 +109,7 @@ public final class PluginManager {
|
||||
fileReader.close();
|
||||
|
||||
// Check if the plugin has alternate dependencies.
|
||||
if(pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
|
||||
if (pluginConfig.loadAfter != null && pluginConfig.loadAfter.length > 0) {
|
||||
// Add the plugin to a "load later" list.
|
||||
dependencies.add(new PluginData(
|
||||
pluginInstance, PluginIdentifier.fromPluginConfig(pluginConfig),
|
||||
@ -131,9 +131,9 @@ public final class PluginManager {
|
||||
|
||||
// Load plugins with dependencies.
|
||||
int depth = 0; final int maxDepth = 30;
|
||||
while(!dependencies.isEmpty()) {
|
||||
while (!dependencies.isEmpty()) {
|
||||
// Check if the depth is too high.
|
||||
if(depth >= maxDepth) {
|
||||
if (depth >= maxDepth) {
|
||||
Grasscutter.getLogger().error("Failed to load plugins with dependencies.");
|
||||
break;
|
||||
}
|
||||
@ -143,7 +143,7 @@ public final class PluginManager {
|
||||
var pluginData = dependencies.get(0);
|
||||
|
||||
// Check if the plugin's dependencies are loaded.
|
||||
if(!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
|
||||
if (!this.plugins.keySet().containsAll(List.of(pluginData.getDependencies()))) {
|
||||
depth++; // Increase depth counter.
|
||||
continue; // Continue to next plugin.
|
||||
}
|
||||
|
@ -19,13 +19,13 @@ public final class PlayerHook {
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* Hooks into the player.
|
||||
* Hooks into the player.
|
||||
* @param player The player to hook into.
|
||||
*/
|
||||
public PlayerHook(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Kicks a player from the server.
|
||||
* TODO: Refactor to kick using a packet.
|
||||
@ -82,7 +82,7 @@ public final class PlayerHook {
|
||||
*/
|
||||
public void teleport(Position position) {
|
||||
this.player.getPosition().set(position);
|
||||
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
|
||||
this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player,
|
||||
EnterType.ENTER_TYPE_JUMP, EnterReason.TransPoint,
|
||||
this.player.getSceneId(), position
|
||||
));
|
||||
@ -111,4 +111,4 @@ public final class PlayerHook {
|
||||
public Avatar getCurrentAvatar() {
|
||||
return this.getCurrentAvatarEntity().getAvatar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,61 +22,61 @@ import java.util.stream.Collectors;
|
||||
@ToString
|
||||
@Setter
|
||||
public class SceneBlock {
|
||||
public int id;
|
||||
public Position max;
|
||||
public Position min;
|
||||
public int id;
|
||||
public Position max;
|
||||
public Position min;
|
||||
|
||||
public int sceneId;
|
||||
public Map<Integer,SceneGroup> groups;
|
||||
public RTree<SceneGroup, Geometry> sceneGroupIndex;
|
||||
public int sceneId;
|
||||
public Map<Integer,SceneGroup> groups;
|
||||
public RTree<SceneGroup, Geometry> sceneGroupIndex;
|
||||
|
||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||
|
||||
public boolean isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
public boolean isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
public void setLoaded(boolean loaded) {
|
||||
this.loaded = loaded;
|
||||
}
|
||||
public void setLoaded(boolean loaded) {
|
||||
this.loaded = loaded;
|
||||
}
|
||||
|
||||
public boolean contains(Position pos) {
|
||||
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
|
||||
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
|
||||
}
|
||||
public boolean contains(Position pos) {
|
||||
return pos.getX() <= this.max.getX() && pos.getX() >= this.min.getX() &&
|
||||
pos.getZ() <= this.max.getZ() && pos.getZ() >= this.min.getZ();
|
||||
}
|
||||
|
||||
public SceneBlock load(int sceneId, Bindings bindings){
|
||||
if(this.loaded){
|
||||
return this;
|
||||
}
|
||||
this.sceneId = sceneId;
|
||||
public SceneBlock load(int sceneId, Bindings bindings) {
|
||||
if (this.loaded) {
|
||||
return this;
|
||||
}
|
||||
this.sceneId = sceneId;
|
||||
this.setLoaded(true);
|
||||
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_block" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
|
||||
if (cs == null) {
|
||||
return null;
|
||||
}
|
||||
if (cs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Eval script
|
||||
try {
|
||||
cs.eval(bindings);
|
||||
// Eval script
|
||||
try {
|
||||
cs.eval(bindings);
|
||||
|
||||
// Set groups
|
||||
// Set groups
|
||||
this.groups = ScriptLoader.getSerializer().toList(SceneGroup.class, bindings.get("groups")).stream()
|
||||
.collect(Collectors.toMap(x -> x.id, y -> y));
|
||||
.collect(Collectors.toMap(x -> x.id, y -> y));
|
||||
|
||||
this.groups.values().forEach(g -> g.block_id = this.id);
|
||||
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
|
||||
} catch (ScriptException exception) {
|
||||
this.sceneGroupIndex = SceneIndexManager.buildIndex(3, this.groups.values(), g -> g.pos.toPoint());
|
||||
} catch (ScriptException exception) {
|
||||
Grasscutter.getLogger().error("An error occurred while loading block " + this.id + " in scene " + sceneId, exception);
|
||||
}
|
||||
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
Grasscutter.getLogger().debug("Successfully loaded block {} in scene {}.", this.id, sceneId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Rectangle toRectangle() {
|
||||
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
|
||||
}
|
||||
}
|
||||
public Rectangle toRectangle() {
|
||||
return Rectangle.create(this.min.toXZDoubleArray(), this.max.toXZDoubleArray());
|
||||
}
|
||||
}
|
||||
|
@ -21,93 +21,93 @@ import java.util.stream.Collectors;
|
||||
@ToString
|
||||
@Setter
|
||||
public class SceneGroup {
|
||||
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
|
||||
public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference
|
||||
|
||||
public int id;
|
||||
public int refresh_id;
|
||||
public Position pos;
|
||||
public int id;
|
||||
public int refresh_id;
|
||||
public Position pos;
|
||||
|
||||
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
|
||||
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
|
||||
public Map<String, SceneTrigger> triggers;
|
||||
public Map<Integer,SceneMonster> monsters; // <ConfigId, Monster>
|
||||
public Map<Integer, SceneGadget> gadgets; // <ConfigId, Gadgets>
|
||||
public Map<String, SceneTrigger> triggers;
|
||||
public Map<Integer, SceneRegion> regions;
|
||||
public List<SceneSuite> suites;
|
||||
public List<SceneVar> variables;
|
||||
public List<SceneSuite> suites;
|
||||
public List<SceneVar> variables;
|
||||
|
||||
public SceneBusiness business;
|
||||
public SceneGarbage garbages;
|
||||
public SceneInitConfig init_config;
|
||||
public SceneBusiness business;
|
||||
public SceneGarbage garbages;
|
||||
public SceneInitConfig init_config;
|
||||
|
||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||
private transient CompiledScript script;
|
||||
private transient Bindings bindings;
|
||||
public static SceneGroup of(int groupId) {
|
||||
var group = new SceneGroup();
|
||||
group.id = groupId;
|
||||
return group;
|
||||
}
|
||||
private transient boolean loaded; // Not an actual variable in the scripts either
|
||||
private transient CompiledScript script;
|
||||
private transient Bindings bindings;
|
||||
public static SceneGroup of(int groupId) {
|
||||
var group = new SceneGroup();
|
||||
group.id = groupId;
|
||||
return group;
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
public boolean isLoaded() {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
public void setLoaded(boolean loaded) {
|
||||
this.loaded = loaded;
|
||||
}
|
||||
public void setLoaded(boolean loaded) {
|
||||
this.loaded = loaded;
|
||||
}
|
||||
|
||||
public int getBusinessType() {
|
||||
return this.business == null ? 0 : this.business.type;
|
||||
}
|
||||
public int getBusinessType() {
|
||||
return this.business == null ? 0 : this.business.type;
|
||||
}
|
||||
|
||||
public List<SceneGadget> getGarbageGadgets() {
|
||||
return this.garbages == null ? null : this.garbages.gadgets;
|
||||
}
|
||||
public List<SceneGadget> getGarbageGadgets() {
|
||||
return this.garbages == null ? null : this.garbages.gadgets;
|
||||
}
|
||||
|
||||
public CompiledScript getScript() {
|
||||
return this.script;
|
||||
}
|
||||
public CompiledScript getScript() {
|
||||
return this.script;
|
||||
}
|
||||
|
||||
public SceneSuite getSuiteByIndex(int index) {
|
||||
return this.suites.get(index - 1);
|
||||
}
|
||||
public SceneSuite getSuiteByIndex(int index) {
|
||||
return this.suites.get(index - 1);
|
||||
}
|
||||
|
||||
public Bindings getBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
public Bindings getBindings() {
|
||||
return this.bindings;
|
||||
}
|
||||
|
||||
public synchronized SceneGroup load(int sceneId){
|
||||
if(this.loaded){
|
||||
return this;
|
||||
}
|
||||
// Set flag here so if there is no script, we don't call this function over and over again.
|
||||
public synchronized SceneGroup load(int sceneId) {
|
||||
if (this.loaded) {
|
||||
return this;
|
||||
}
|
||||
// Set flag here so if there is no script, we don't call this function over and over again.
|
||||
this.setLoaded(true);
|
||||
|
||||
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||
this.bindings = ScriptLoader.getEngine().createBindings();
|
||||
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "_group" + this.id + "." + ScriptLoader.getScriptType()));
|
||||
|
||||
if (cs == null) {
|
||||
return this;
|
||||
}
|
||||
if (cs == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.script = cs;
|
||||
this.script = cs;
|
||||
|
||||
// Eval script
|
||||
try {
|
||||
cs.eval(this.bindings);
|
||||
// Eval script
|
||||
try {
|
||||
cs.eval(this.bindings);
|
||||
|
||||
// Set
|
||||
// Set
|
||||
this.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, this.bindings.get("monsters")).stream()
|
||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||
this.monsters.values().forEach(m -> m.group = this);
|
||||
|
||||
this.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, this.bindings.get("gadgets")).stream()
|
||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||
.collect(Collectors.toMap(x -> x.config_id, y -> y));
|
||||
this.gadgets.values().forEach(m -> m.group = this);
|
||||
|
||||
this.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, this.bindings.get("triggers")).stream()
|
||||
.collect(Collectors.toMap(x -> x.name, y -> y));
|
||||
.collect(Collectors.toMap(x -> x.name, y -> y));
|
||||
this.triggers.values().forEach(t -> t.currentGroup = this);
|
||||
|
||||
this.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, this.bindings.get("suites"));
|
||||
@ -117,35 +117,35 @@ public class SceneGroup {
|
||||
|
||||
this.init_config = ScriptLoader.getSerializer().toObject(SceneInitConfig.class, this.bindings.get("init_config"));
|
||||
|
||||
// Garbages // TODO: fix properly later
|
||||
Object garbagesValue = this.bindings.get("garbages");
|
||||
if (garbagesValue instanceof LuaValue garbagesTable) {
|
||||
// Garbages // TODO: fix properly later
|
||||
Object garbagesValue = this.bindings.get("garbages");
|
||||
if (garbagesValue instanceof LuaValue garbagesTable) {
|
||||
this.garbages = new SceneGarbage();
|
||||
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
|
||||
if (garbagesTable.checktable().get("gadgets") != LuaValue.NIL) {
|
||||
this.garbages.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, garbagesTable.checktable().get("gadgets").checktable());
|
||||
this.garbages.gadgets.forEach(m -> m.group = this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add variables to suite
|
||||
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
|
||||
// Add variables to suite
|
||||
this.variables = ScriptLoader.getSerializer().toList(SceneVar.class, this.bindings.get("variables"));
|
||||
|
||||
// Add monsters and gadgets to suite
|
||||
// Add monsters and gadgets to suite
|
||||
this.suites.forEach(i -> i.init(this));
|
||||
|
||||
} catch (ScriptException e) {
|
||||
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
Grasscutter.getLogger().error("An error occurred while loading group " + this.id + " in scene " + sceneId + ".", e);
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
|
||||
return this;
|
||||
}
|
||||
Grasscutter.getLogger().debug("Successfully loaded group {} in scene {}.", this.id, sceneId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Optional<SceneBossChest> searchBossChestInGroup() {
|
||||
return this.gadgets.values().stream()
|
||||
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
|
||||
.map(g -> g.boss_chest)
|
||||
.findFirst();
|
||||
}
|
||||
public Optional<SceneBossChest> searchBossChestInGroup() {
|
||||
return this.gadgets.values().stream()
|
||||
.filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0)
|
||||
.map(g -> g.boss_chest)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ public class SceneMeta {
|
||||
return new SceneMeta().load(sceneId);
|
||||
}
|
||||
|
||||
public SceneMeta load(int sceneId){
|
||||
public SceneMeta load(int sceneId) {
|
||||
// Get compiled script if cached
|
||||
CompiledScript cs = ScriptLoader.getScriptByPath(
|
||||
SCRIPT("Scene/" + sceneId + "/scene" + sceneId + "." + ScriptLoader.getScriptType()));
|
||||
|
@ -2,11 +2,11 @@ package emu.grasscutter.server.game;
|
||||
|
||||
public abstract class BaseGameSystem {
|
||||
protected final GameServer server;
|
||||
|
||||
|
||||
public BaseGameSystem(GameServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
|
||||
public GameServer getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
@ -51,97 +51,97 @@ import static emu.grasscutter.utils.Language.translate;
|
||||
public final class GameServer extends KcpServer {
|
||||
// Game server base
|
||||
private final InetSocketAddress address;
|
||||
private final GameServerPacketHandler packetHandler;
|
||||
private final GameServerPacketHandler packetHandler;
|
||||
private final Map<Integer, Player> players;
|
||||
private final Set<World> worlds;
|
||||
|
||||
// Server systems
|
||||
private final InventorySystem inventorySystem;
|
||||
private final GachaSystem gachaSystem;
|
||||
private final ShopSystem shopSystem;
|
||||
private final MultiplayerSystem multiplayerSystem;
|
||||
private final DungeonSystem dungeonSystem;
|
||||
private final ExpeditionSystem expeditionSystem;
|
||||
private final DropSystem dropSystem;
|
||||
private final WorldDataSystem worldDataSystem;
|
||||
private final BattlePassSystem battlePassSystem;
|
||||
private final CombineManger combineSystem;
|
||||
private final TowerSystem towerSystem;
|
||||
private final AnnouncementSystem announcementSystem;
|
||||
private final QuestSystem questSystem;
|
||||
|
||||
// Extra
|
||||
private final ServerTaskScheduler scheduler;
|
||||
private final InventorySystem inventorySystem;
|
||||
private final GachaSystem gachaSystem;
|
||||
private final ShopSystem shopSystem;
|
||||
private final MultiplayerSystem multiplayerSystem;
|
||||
private final DungeonSystem dungeonSystem;
|
||||
private final ExpeditionSystem expeditionSystem;
|
||||
private final DropSystem dropSystem;
|
||||
private final WorldDataSystem worldDataSystem;
|
||||
private final BattlePassSystem battlePassSystem;
|
||||
private final CombineManger combineSystem;
|
||||
private final TowerSystem towerSystem;
|
||||
private final AnnouncementSystem announcementSystem;
|
||||
private final QuestSystem questSystem;
|
||||
|
||||
// Extra
|
||||
private final ServerTaskScheduler scheduler;
|
||||
private final CommandMap commandMap;
|
||||
private final TaskMap taskMap;
|
||||
|
||||
private ChatManagerHandler chatManager;
|
||||
|
||||
public GameServer() {
|
||||
this(getAdapterInetSocketAddress());
|
||||
}
|
||||
private ChatManagerHandler chatManager;
|
||||
|
||||
public GameServer(InetSocketAddress address) {
|
||||
ChannelConfig channelConfig = new ChannelConfig();
|
||||
channelConfig.nodelay(true, 40, 2, true);
|
||||
channelConfig.setMtu(1400);
|
||||
channelConfig.setSndwnd(256);
|
||||
channelConfig.setRcvwnd(256);
|
||||
channelConfig.setTimeoutMillis(30 * 1000);//30s
|
||||
channelConfig.setUseConvChannel(true);
|
||||
channelConfig.setAckNoDelay(false);
|
||||
public GameServer() {
|
||||
this(getAdapterInetSocketAddress());
|
||||
}
|
||||
|
||||
this.init(GameSessionManager.getListener(),channelConfig,address);
|
||||
public GameServer(InetSocketAddress address) {
|
||||
ChannelConfig channelConfig = new ChannelConfig();
|
||||
channelConfig.nodelay(true, 40, 2, true);
|
||||
channelConfig.setMtu(1400);
|
||||
channelConfig.setSndwnd(256);
|
||||
channelConfig.setRcvwnd(256);
|
||||
channelConfig.setTimeoutMillis(30 * 1000);//30s
|
||||
channelConfig.setUseConvChannel(true);
|
||||
channelConfig.setAckNoDelay(false);
|
||||
|
||||
DungeonChallenge.initialize();
|
||||
EnergyManager.initialize();
|
||||
StaminaManager.initialize();
|
||||
CookingManager.initialize();
|
||||
CombineManger.initialize();
|
||||
this.init(GameSessionManager.getListener(),channelConfig,address);
|
||||
|
||||
// Game Server base
|
||||
this.address = address;
|
||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||
this.players = new ConcurrentHashMap<>();
|
||||
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
// Extra
|
||||
this.scheduler = new ServerTaskScheduler();
|
||||
this.commandMap = new CommandMap(true);
|
||||
DungeonChallenge.initialize();
|
||||
EnergyManager.initialize();
|
||||
StaminaManager.initialize();
|
||||
CookingManager.initialize();
|
||||
CombineManger.initialize();
|
||||
|
||||
// Game Server base
|
||||
this.address = address;
|
||||
this.packetHandler = new GameServerPacketHandler(PacketHandler.class);
|
||||
this.players = new ConcurrentHashMap<>();
|
||||
this.worlds = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
// Extra
|
||||
this.scheduler = new ServerTaskScheduler();
|
||||
this.commandMap = new CommandMap(true);
|
||||
this.taskMap = new TaskMap(true);
|
||||
|
||||
// Create game systems
|
||||
this.inventorySystem = new InventorySystem(this);
|
||||
this.gachaSystem = new GachaSystem(this);
|
||||
this.shopSystem = new ShopSystem(this);
|
||||
this.multiplayerSystem = new MultiplayerSystem(this);
|
||||
this.dungeonSystem = new DungeonSystem(this);
|
||||
this.dropSystem = new DropSystem(this);
|
||||
this.expeditionSystem = new ExpeditionSystem(this);
|
||||
this.combineSystem = new CombineManger(this);
|
||||
this.towerSystem = new TowerSystem(this);
|
||||
this.worldDataSystem = new WorldDataSystem(this);
|
||||
this.battlePassSystem = new BattlePassSystem(this);
|
||||
this.announcementSystem = new AnnouncementSystem(this);
|
||||
this.questSystem = new QuestSystem(this);
|
||||
|
||||
// Chata manager
|
||||
this.chatManager = new ChatManager(this);
|
||||
|
||||
// Hook into shutdown event.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||
}
|
||||
|
||||
// Create game systems
|
||||
this.inventorySystem = new InventorySystem(this);
|
||||
this.gachaSystem = new GachaSystem(this);
|
||||
this.shopSystem = new ShopSystem(this);
|
||||
this.multiplayerSystem = new MultiplayerSystem(this);
|
||||
this.dungeonSystem = new DungeonSystem(this);
|
||||
this.dropSystem = new DropSystem(this);
|
||||
this.expeditionSystem = new ExpeditionSystem(this);
|
||||
this.combineSystem = new CombineManger(this);
|
||||
this.towerSystem = new TowerSystem(this);
|
||||
this.worldDataSystem = new WorldDataSystem(this);
|
||||
this.battlePassSystem = new BattlePassSystem(this);
|
||||
this.announcementSystem = new AnnouncementSystem(this);
|
||||
this.questSystem = new QuestSystem(this);
|
||||
|
||||
// Chata manager
|
||||
this.chatManager = new ChatManager(this);
|
||||
|
||||
// Hook into shutdown event.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ChatManagerHandler getChatManager() {
|
||||
return chatManager;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public void setChatManager(ChatManagerHandler chatManager) {
|
||||
this.chatManager = chatManager;
|
||||
}
|
||||
|
||||
|
||||
public ChatManagerHandler getChatSystem() {
|
||||
return chatManager;
|
||||
}
|
||||
@ -150,71 +150,71 @@ public final class GameServer extends KcpServer {
|
||||
this.chatManager = chatManager;
|
||||
}
|
||||
|
||||
private static InetSocketAddress getAdapterInetSocketAddress(){
|
||||
InetSocketAddress inetSocketAddress;
|
||||
if(GAME_INFO.bindAddress.equals("")){
|
||||
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
||||
}else{
|
||||
inetSocketAddress=new InetSocketAddress(
|
||||
GAME_INFO.bindAddress,
|
||||
GAME_INFO.bindPort
|
||||
);
|
||||
}
|
||||
return inetSocketAddress;
|
||||
}
|
||||
private static InetSocketAddress getAdapterInetSocketAddress() {
|
||||
InetSocketAddress inetSocketAddress;
|
||||
if (GAME_INFO.bindAddress.equals("")) {
|
||||
inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort);
|
||||
}else {
|
||||
inetSocketAddress=new InetSocketAddress(
|
||||
GAME_INFO.bindAddress,
|
||||
GAME_INFO.bindPort
|
||||
);
|
||||
}
|
||||
return inetSocketAddress;
|
||||
}
|
||||
|
||||
public void registerPlayer(Player player) {
|
||||
getPlayers().put(player.getUid(), player);
|
||||
}
|
||||
public void registerPlayer(Player player) {
|
||||
getPlayers().put(player.getUid(), player);
|
||||
}
|
||||
|
||||
public Player getPlayerByUid(int id) {
|
||||
return this.getPlayerByUid(id, false);
|
||||
}
|
||||
public Player getPlayerByUid(int id) {
|
||||
return this.getPlayerByUid(id, false);
|
||||
}
|
||||
|
||||
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
||||
// Console check
|
||||
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
||||
return null;
|
||||
}
|
||||
public Player getPlayerByUid(int id, boolean allowOfflinePlayers) {
|
||||
// Console check
|
||||
if (id == GameConstants.SERVER_CONSOLE_UID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get from online players
|
||||
Player player = this.getPlayers().get(id);
|
||||
// Get from online players
|
||||
Player player = this.getPlayers().get(id);
|
||||
|
||||
if (!allowOfflinePlayers) {
|
||||
return player;
|
||||
}
|
||||
if (!allowOfflinePlayers) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Check database if character isnt here
|
||||
if (player == null) {
|
||||
player = DatabaseHelper.getPlayerByUid(id);
|
||||
}
|
||||
// Check database if character isnt here
|
||||
if (player == null) {
|
||||
player = DatabaseHelper.getPlayerByUid(id);
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
return player;
|
||||
}
|
||||
|
||||
public Player getPlayerByAccountId(String accountId) {
|
||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
|
||||
return playerOpt.orElse(null);
|
||||
}
|
||||
public Player getPlayerByAccountId(String accountId) {
|
||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst();
|
||||
return playerOpt.orElse(null);
|
||||
}
|
||||
|
||||
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
||||
// Get from online players
|
||||
Player player = this.getPlayerByUid(id, true);
|
||||
public SocialDetail.Builder getSocialDetailByUid(int id) {
|
||||
// Get from online players
|
||||
Player player = this.getPlayerByUid(id, true);
|
||||
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return player.getSocialDetail();
|
||||
}
|
||||
return player.getSocialDetail();
|
||||
}
|
||||
|
||||
public Account getAccountByName(String username) {
|
||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
|
||||
if (playerOpt.isPresent()) {
|
||||
return playerOpt.get().getAccount();
|
||||
}
|
||||
return DatabaseHelper.getAccountByName(username);
|
||||
}
|
||||
public Account getAccountByName(String username) {
|
||||
Optional<Player> playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getUsername().equals(username)).findFirst();
|
||||
if (playerOpt.isPresent()) {
|
||||
return playerOpt.get().getAccount();
|
||||
}
|
||||
return DatabaseHelper.getAccountByName(username);
|
||||
}
|
||||
|
||||
public synchronized void onTick() {
|
||||
var tickStart = Instant.now();
|
||||
@ -244,43 +244,43 @@ public final class GameServer extends KcpServer {
|
||||
event.call();
|
||||
}
|
||||
|
||||
public void registerWorld(World world) {
|
||||
this.getWorlds().add(world);
|
||||
}
|
||||
public void registerWorld(World world) {
|
||||
this.getWorlds().add(world);
|
||||
}
|
||||
|
||||
public void deregisterWorld(World world) {
|
||||
// TODO Auto-generated method stub
|
||||
public void deregisterWorld(World world) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
// Schedule game loop.
|
||||
Timer gameLoop = new Timer();
|
||||
gameLoop.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
onTick();
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
|
||||
}
|
||||
}
|
||||
}, new Date(), 1000L);
|
||||
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
||||
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
|
||||
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
||||
event.call();
|
||||
}
|
||||
public void start() {
|
||||
// Schedule game loop.
|
||||
Timer gameLoop = new Timer();
|
||||
gameLoop.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
onTick();
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error(translate("messages.game.game_update_error"), e);
|
||||
}
|
||||
}
|
||||
}, new Date(), 1000L);
|
||||
Grasscutter.getLogger().info(translate("messages.status.free_software"));
|
||||
Grasscutter.getLogger().info(translate("messages.game.port_bind", Integer.toString(address.getPort())));
|
||||
ServerStartEvent event = new ServerStartEvent(ServerEvent.Type.GAME, OffsetDateTime.now());
|
||||
event.call();
|
||||
}
|
||||
|
||||
public void onServerShutdown() {
|
||||
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
||||
public void onServerShutdown() {
|
||||
ServerStopEvent event = new ServerStopEvent(ServerEvent.Type.GAME, OffsetDateTime.now()); event.call();
|
||||
|
||||
// Kick and save all players
|
||||
List<Player> list = new ArrayList<>(this.getPlayers().size());
|
||||
list.addAll(this.getPlayers().values());
|
||||
// Kick and save all players
|
||||
List<Player> list = new ArrayList<>(this.getPlayers().size());
|
||||
list.addAll(this.getPlayers().values());
|
||||
|
||||
for (Player player : list) {
|
||||
player.getSession().close();
|
||||
}
|
||||
}
|
||||
for (Player player : list) {
|
||||
player.getSession().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,84 +18,84 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class GameServerPacketHandler {
|
||||
private final Int2ObjectMap<PacketHandler> handlers;
|
||||
private final Int2ObjectMap<PacketHandler> handlers;
|
||||
|
||||
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
|
||||
this.handlers = new Int2ObjectOpenHashMap<>();
|
||||
public GameServerPacketHandler(Class<? extends PacketHandler> handlerClass) {
|
||||
this.handlers = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
this.registerHandlers(handlerClass);
|
||||
}
|
||||
this.registerHandlers(handlerClass);
|
||||
}
|
||||
|
||||
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
|
||||
try {
|
||||
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
|
||||
public void registerPacketHandler(Class<? extends PacketHandler> handlerClass) {
|
||||
try {
|
||||
Opcodes opcode = handlerClass.getAnnotation(Opcodes.class);
|
||||
|
||||
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
|
||||
return;
|
||||
}
|
||||
if (opcode == null || opcode.disabled() || opcode.value() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
|
||||
PacketHandler packetHandler = handlerClass.getDeclaredConstructor().newInstance();
|
||||
|
||||
this.handlers.put(opcode.value(), packetHandler);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
this.handlers.put(opcode.value(), packetHandler);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
|
||||
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
|
||||
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
|
||||
public void registerHandlers(Class<? extends PacketHandler> handlerClass) {
|
||||
Reflections reflections = new Reflections("emu.grasscutter.server.packet");
|
||||
Set<?> handlerClasses = reflections.getSubTypesOf(handlerClass);
|
||||
|
||||
for (Object obj : handlerClasses) {
|
||||
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
|
||||
}
|
||||
for (Object obj : handlerClasses) {
|
||||
this.registerPacketHandler((Class<? extends PacketHandler>) obj);
|
||||
}
|
||||
|
||||
// Debug
|
||||
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
|
||||
}
|
||||
// Debug
|
||||
Grasscutter.getLogger().debug("Registered " + this.handlers.size() + " " + handlerClass.getSimpleName() + "s");
|
||||
}
|
||||
|
||||
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
|
||||
PacketHandler handler = this.handlers.get(opcode);
|
||||
public void handle(GameSession session, int opcode, byte[] header, byte[] payload) {
|
||||
PacketHandler handler = this.handlers.get(opcode);
|
||||
|
||||
if (handler != null) {
|
||||
try {
|
||||
// Make sure session is ready for packets
|
||||
SessionState state = session.getState();
|
||||
if (handler != null) {
|
||||
try {
|
||||
// Make sure session is ready for packets
|
||||
SessionState state = session.getState();
|
||||
|
||||
if (opcode == PacketOpcodes.PingReq) {
|
||||
// Always continue if packet is ping request
|
||||
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
|
||||
if (state != SessionState.WAITING_FOR_TOKEN) {
|
||||
return;
|
||||
}
|
||||
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
|
||||
if (state != SessionState.WAITING_FOR_LOGIN) {
|
||||
return;
|
||||
}
|
||||
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
|
||||
if (state != SessionState.PICKING_CHARACTER) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (state != SessionState.ACTIVE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (opcode == PacketOpcodes.PingReq) {
|
||||
// Always continue if packet is ping request
|
||||
} else if (opcode == PacketOpcodes.GetPlayerTokenReq) {
|
||||
if (state != SessionState.WAITING_FOR_TOKEN) {
|
||||
return;
|
||||
}
|
||||
} else if (opcode == PacketOpcodes.PlayerLoginReq) {
|
||||
if (state != SessionState.WAITING_FOR_LOGIN) {
|
||||
return;
|
||||
}
|
||||
} else if (opcode == PacketOpcodes.SetPlayerBornDataReq) {
|
||||
if (state != SessionState.PICKING_CHARACTER) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (state != SessionState.ACTIVE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke event.
|
||||
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
|
||||
if(!event.isCanceled()) // If event is not canceled, continue.
|
||||
handler.handle(session, header, event.getPacketData());
|
||||
} catch (Exception ex) {
|
||||
// TODO Remove this when no more needed
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return; // Packet successfully handled
|
||||
}
|
||||
// Invoke event.
|
||||
ReceivePacketEvent event = new ReceivePacketEvent(session, opcode, payload); event.call();
|
||||
if (!event.isCanceled()) // If event is not canceled, continue.
|
||||
handler.handle(session, header, event.getPacketData());
|
||||
} catch (Exception ex) {
|
||||
// TODO Remove this when no more needed
|
||||
ex.printStackTrace();
|
||||
}
|
||||
return; // Packet successfully handled
|
||||
}
|
||||
|
||||
// Log unhandled packets
|
||||
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
|
||||
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
|
||||
}
|
||||
}
|
||||
// Log unhandled packets
|
||||
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) {
|
||||
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,108 +21,108 @@ import static emu.grasscutter.config.Configuration.*;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
public class GameSession implements GameSessionManager.KcpChannel {
|
||||
private final GameServer server;
|
||||
private GameSessionManager.KcpTunnel tunnel;
|
||||
private final GameServer server;
|
||||
private GameSessionManager.KcpTunnel tunnel;
|
||||
|
||||
private Account account;
|
||||
private Player player;
|
||||
private Account account;
|
||||
private Player player;
|
||||
|
||||
private boolean useSecretKey;
|
||||
private SessionState state;
|
||||
private boolean useSecretKey;
|
||||
private SessionState state;
|
||||
|
||||
private int clientTime;
|
||||
private long lastPingTime;
|
||||
private int lastClientSeq = 10;
|
||||
private int clientTime;
|
||||
private long lastPingTime;
|
||||
private int lastClientSeq = 10;
|
||||
|
||||
public GameSession(GameServer server) {
|
||||
this.server = server;
|
||||
this.state = SessionState.WAITING_FOR_TOKEN;
|
||||
this.lastPingTime = System.currentTimeMillis();
|
||||
}
|
||||
public GameSession(GameServer server) {
|
||||
this.server = server;
|
||||
this.state = SessionState.WAITING_FOR_TOKEN;
|
||||
this.lastPingTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
public GameServer getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
try{
|
||||
return tunnel.getAddress();
|
||||
}catch (Throwable ignore){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public InetSocketAddress getAddress() {
|
||||
try {
|
||||
return tunnel.getAddress();
|
||||
}catch (Throwable ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean useSecretKey() {
|
||||
return useSecretKey;
|
||||
}
|
||||
public boolean useSecretKey() {
|
||||
return useSecretKey;
|
||||
}
|
||||
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
public Account getAccount() {
|
||||
return account;
|
||||
}
|
||||
|
||||
public void setAccount(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
public void setAccount(Account account) {
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public String getAccountId() {
|
||||
return this.getAccount().getId();
|
||||
}
|
||||
public String getAccountId() {
|
||||
return this.getAccount().getId();
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public synchronized void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.player.setSession(this);
|
||||
this.player.setAccount(this.getAccount());
|
||||
}
|
||||
public synchronized void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
this.player.setSession(this);
|
||||
this.player.setAccount(this.getAccount());
|
||||
}
|
||||
|
||||
public SessionState getState() {
|
||||
return state;
|
||||
}
|
||||
public SessionState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(SessionState state) {
|
||||
this.state = state;
|
||||
}
|
||||
public void setState(SessionState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return this.getPlayer() != null;
|
||||
}
|
||||
public boolean isLoggedIn() {
|
||||
return this.getPlayer() != null;
|
||||
}
|
||||
|
||||
public void setUseSecretKey(boolean useSecretKey) {
|
||||
this.useSecretKey = useSecretKey;
|
||||
}
|
||||
public void setUseSecretKey(boolean useSecretKey) {
|
||||
this.useSecretKey = useSecretKey;
|
||||
}
|
||||
|
||||
public int getClientTime() {
|
||||
return this.clientTime;
|
||||
}
|
||||
public int getClientTime() {
|
||||
return this.clientTime;
|
||||
}
|
||||
|
||||
public long getLastPingTime() {
|
||||
return lastPingTime;
|
||||
}
|
||||
public long getLastPingTime() {
|
||||
return lastPingTime;
|
||||
}
|
||||
|
||||
public void updateLastPingTime(int clientTime) {
|
||||
this.clientTime = clientTime;
|
||||
this.lastPingTime = System.currentTimeMillis();
|
||||
}
|
||||
public void updateLastPingTime(int clientTime) {
|
||||
this.clientTime = clientTime;
|
||||
this.lastPingTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public int getNextClientSequence() {
|
||||
return ++lastClientSeq;
|
||||
}
|
||||
public int getNextClientSequence() {
|
||||
return ++lastClientSeq;
|
||||
}
|
||||
|
||||
public void replayPacket(int opcode, String name) {
|
||||
String filePath = PACKET(name);
|
||||
File p = new File(filePath);
|
||||
String filePath = PACKET(name);
|
||||
File p = new File(filePath);
|
||||
|
||||
if (!p.exists()) return;
|
||||
if (!p.exists()) return;
|
||||
|
||||
byte[] packet = FileUtils.read(p);
|
||||
byte[] packet = FileUtils.read(p);
|
||||
|
||||
BasePacket basePacket = new BasePacket(opcode);
|
||||
basePacket.setData(packet);
|
||||
BasePacket basePacket = new BasePacket(opcode);
|
||||
basePacket.setData(packet);
|
||||
|
||||
send(basePacket);
|
||||
send(basePacket);
|
||||
}
|
||||
|
||||
public void logPacket( String sendOrRecv, int opcode, byte[] payload) {
|
||||
@ -130,162 +130,162 @@ public class GameSession implements GameSessionManager.KcpChannel {
|
||||
System.out.println(Utils.bytesToHex(payload));
|
||||
}
|
||||
public void send(BasePacket packet) {
|
||||
// Test
|
||||
if (packet.getOpcode() <= 0) {
|
||||
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
|
||||
return;
|
||||
}
|
||||
// Test
|
||||
if (packet.getOpcode() <= 0) {
|
||||
Grasscutter.getLogger().warn("Tried to send packet with missing cmd id!");
|
||||
return;
|
||||
}
|
||||
|
||||
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
|
||||
// Stop WindSeedClientNotify from being sent for security purposes.
|
||||
if(PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
|
||||
return;
|
||||
}
|
||||
// DO NOT REMOVE (unless we find a way to validate code before sending to client which I don't think we can)
|
||||
// Stop WindSeedClientNotify from being sent for security purposes.
|
||||
if (PacketOpcodesUtils.BANNED_PACKETS.contains(packet.getOpcode())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Header
|
||||
if (packet.shouldBuildHeader()) {
|
||||
packet.buildHeader(this.getNextClientSequence());
|
||||
}
|
||||
// Header
|
||||
if (packet.shouldBuildHeader()) {
|
||||
packet.buildHeader(this.getNextClientSequence());
|
||||
}
|
||||
|
||||
// Log
|
||||
switch (GAME_INFO.logPackets) {
|
||||
case ALL -> {
|
||||
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
|
||||
// Log
|
||||
switch (GAME_INFO.logPackets) {
|
||||
case ALL -> {
|
||||
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) {
|
||||
logPacket("SEND", packet.getOpcode(), packet.getData());
|
||||
}
|
||||
}
|
||||
case WHITELIST-> {
|
||||
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
|
||||
logPacket("SEND", packet.getOpcode(), packet.getData());
|
||||
}
|
||||
}
|
||||
case BLACKLIST-> {
|
||||
}
|
||||
case WHITELIST-> {
|
||||
if (SERVER.debugWhitelist.contains(packet.getOpcode())) {
|
||||
logPacket("SEND", packet.getOpcode(), packet.getData());
|
||||
}
|
||||
}
|
||||
case BLACKLIST-> {
|
||||
if (!SERVER.debugBlacklist.contains(packet.getOpcode())) {
|
||||
logPacket("SEND", packet.getOpcode(), packet.getData());
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
|
||||
// Invoke event.
|
||||
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
|
||||
if(!event.isCanceled()) { // If event is not cancelled, continue.
|
||||
tunnel.writeData(event.getPacket().build());
|
||||
}
|
||||
// Invoke event.
|
||||
SendPacketEvent event = new SendPacketEvent(this, packet); event.call();
|
||||
if (!event.isCanceled()) { // If event is not cancelled, continue.
|
||||
tunnel.writeData(event.getPacket().build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
|
||||
this.tunnel = tunnel;
|
||||
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
|
||||
}
|
||||
@Override
|
||||
public void onConnected(GameSessionManager.KcpTunnel tunnel) {
|
||||
this.tunnel = tunnel;
|
||||
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleReceive(byte[] bytes) {
|
||||
// Decrypt and turn back into a packet
|
||||
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
|
||||
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
|
||||
@Override
|
||||
public void handleReceive(byte[] bytes) {
|
||||
// Decrypt and turn back into a packet
|
||||
Crypto.xor(bytes, useSecretKey() ? Crypto.ENCRYPT_KEY : Crypto.DISPATCH_KEY);
|
||||
ByteBuf packet = Unpooled.wrappedBuffer(bytes);
|
||||
|
||||
// Log
|
||||
//logPacket(packet);
|
||||
// Handle
|
||||
try {
|
||||
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
|
||||
while (packet.readableBytes() > 0) {
|
||||
// Length
|
||||
if (packet.readableBytes() < 12) {
|
||||
return;
|
||||
}
|
||||
// Packet sanity check
|
||||
int const1 = packet.readShort();
|
||||
if (const1 != 17767) {
|
||||
if(allDebug){
|
||||
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
|
||||
}
|
||||
return; // Bad packet
|
||||
}
|
||||
// Data
|
||||
int opcode = packet.readShort();
|
||||
int headerLength = packet.readShort();
|
||||
int payloadLength = packet.readInt();
|
||||
byte[] header = new byte[headerLength];
|
||||
byte[] payload = new byte[payloadLength];
|
||||
// Log
|
||||
//logPacket(packet);
|
||||
// Handle
|
||||
try {
|
||||
boolean allDebug = GAME_INFO.logPackets == ServerDebugMode.ALL;
|
||||
while (packet.readableBytes() > 0) {
|
||||
// Length
|
||||
if (packet.readableBytes() < 12) {
|
||||
return;
|
||||
}
|
||||
// Packet sanity check
|
||||
int const1 = packet.readShort();
|
||||
if (const1 != 17767) {
|
||||
if (allDebug) {
|
||||
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect 17767",const1);
|
||||
}
|
||||
return; // Bad packet
|
||||
}
|
||||
// Data
|
||||
int opcode = packet.readShort();
|
||||
int headerLength = packet.readShort();
|
||||
int payloadLength = packet.readInt();
|
||||
byte[] header = new byte[headerLength];
|
||||
byte[] payload = new byte[payloadLength];
|
||||
|
||||
packet.readBytes(header);
|
||||
packet.readBytes(payload);
|
||||
// Sanity check #2
|
||||
int const2 = packet.readShort();
|
||||
if (const2 != -30293) {
|
||||
if(allDebug){
|
||||
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
|
||||
}
|
||||
return; // Bad packet
|
||||
}
|
||||
|
||||
// Log packet
|
||||
switch (GAME_INFO.logPackets) {
|
||||
case ALL -> {
|
||||
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
|
||||
packet.readBytes(header);
|
||||
packet.readBytes(payload);
|
||||
// Sanity check #2
|
||||
int const2 = packet.readShort();
|
||||
if (const2 != -30293) {
|
||||
if (allDebug) {
|
||||
Grasscutter.getLogger().error("Bad Data Package Received: got {} ,expect -30293",const2);
|
||||
}
|
||||
return; // Bad packet
|
||||
}
|
||||
|
||||
// Log packet
|
||||
switch (GAME_INFO.logPackets) {
|
||||
case ALL -> {
|
||||
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) {
|
||||
logPacket("RECV",opcode, payload);
|
||||
}
|
||||
}
|
||||
case WHITELIST-> {
|
||||
if (SERVER.debugWhitelist.contains(opcode)) {
|
||||
logPacket("RECV",opcode, payload);
|
||||
}
|
||||
}
|
||||
case BLACKLIST-> {
|
||||
if (!(SERVER.debugBlacklist.contains(opcode))) {
|
||||
logPacket("RECV",opcode, payload);
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
case WHITELIST-> {
|
||||
if (SERVER.debugWhitelist.contains(opcode)) {
|
||||
logPacket("RECV",opcode, payload);
|
||||
}
|
||||
}
|
||||
case BLACKLIST-> {
|
||||
if (!(SERVER.debugBlacklist.contains(opcode))) {
|
||||
logPacket("RECV",opcode, payload);
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
|
||||
// Handle
|
||||
getServer().getPacketHandler().handle(this, opcode, header, payload);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
//byteBuf.release(); //Needn't
|
||||
packet.release();
|
||||
}
|
||||
}
|
||||
// Handle
|
||||
getServer().getPacketHandler().handle(this, opcode, header, payload);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
//byteBuf.release(); //Needn't
|
||||
packet.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClose() {
|
||||
setState(SessionState.INACTIVE);
|
||||
//send disconnection pack in case of reconnection
|
||||
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
|
||||
// Save after disconnecting
|
||||
if (this.isLoggedIn()) {
|
||||
Player player = getPlayer();
|
||||
// Call logout event.
|
||||
player.onLogout();
|
||||
}
|
||||
try {
|
||||
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
|
||||
}catch (Throwable ignore){
|
||||
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
|
||||
}
|
||||
tunnel = null;
|
||||
}
|
||||
@Override
|
||||
public void handleClose() {
|
||||
setState(SessionState.INACTIVE);
|
||||
//send disconnection pack in case of reconnection
|
||||
Grasscutter.getLogger().info(translate("messages.game.disconnect", this.getAddress().toString()));
|
||||
// Save after disconnecting
|
||||
if (this.isLoggedIn()) {
|
||||
Player player = getPlayer();
|
||||
// Call logout event.
|
||||
player.onLogout();
|
||||
}
|
||||
try {
|
||||
send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify));
|
||||
}catch (Throwable ignore) {
|
||||
Grasscutter.getLogger().warn("closing {} error",getAddress().getAddress().getHostAddress());
|
||||
}
|
||||
tunnel = null;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
tunnel.close();
|
||||
}
|
||||
public void close() {
|
||||
tunnel.close();
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return getState() == SessionState.ACTIVE;
|
||||
}
|
||||
public boolean isActive() {
|
||||
return getState() == SessionState.ACTIVE;
|
||||
}
|
||||
|
||||
public enum SessionState {
|
||||
INACTIVE,
|
||||
WAITING_FOR_TOKEN,
|
||||
WAITING_FOR_LOGIN,
|
||||
PICKING_CHARACTER,
|
||||
ACTIVE
|
||||
}
|
||||
public enum SessionState {
|
||||
INACTIVE,
|
||||
WAITING_FOR_TOKEN,
|
||||
WAITING_FOR_LOGIN,
|
||||
PICKING_CHARACTER,
|
||||
ACTIVE
|
||||
}
|
||||
}
|
||||
|
@ -30,22 +30,22 @@ public final class HttpServer {
|
||||
this.express = new Express(config -> {
|
||||
// Set the Express HTTP server.
|
||||
config.server(HttpServer::createServer);
|
||||
|
||||
|
||||
// Configure encryption/HTTPS/SSL.
|
||||
config.enforceSsl = HTTP_ENCRYPTION.useEncryption;
|
||||
|
||||
|
||||
// Configure HTTP policies.
|
||||
if(HTTP_POLICIES.cors.enabled) {
|
||||
if (HTTP_POLICIES.cors.enabled) {
|
||||
var allowedOrigins = HTTP_POLICIES.cors.allowedOrigins;
|
||||
if (allowedOrigins.length > 0)
|
||||
config.enableCorsForOrigin(allowedOrigins);
|
||||
else config.enableCorsForAllOrigins();
|
||||
}
|
||||
|
||||
|
||||
// Configure debug logging.
|
||||
if(DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
|
||||
if (DISPATCH_INFO.logRequests == ServerDebugMode.ALL)
|
||||
config.enableDevLogging();
|
||||
|
||||
|
||||
// Disable compression on static files.
|
||||
config.precompressStaticFiles = false;
|
||||
});
|
||||
@ -60,26 +60,26 @@ public final class HttpServer {
|
||||
Server server = new Server();
|
||||
ServerConnector serverConnector
|
||||
= new ServerConnector(server);
|
||||
|
||||
if(HTTP_ENCRYPTION.useEncryption) {
|
||||
|
||||
if (HTTP_ENCRYPTION.useEncryption) {
|
||||
var sslContextFactory = new SslContextFactory.Server();
|
||||
var keystoreFile = new File(HTTP_ENCRYPTION.keystore);
|
||||
|
||||
if(!keystoreFile.exists()) {
|
||||
|
||||
if (!keystoreFile.exists()) {
|
||||
HTTP_ENCRYPTION.useEncryption = false;
|
||||
HTTP_ENCRYPTION.useInRouting = false;
|
||||
|
||||
|
||||
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error"));
|
||||
} else try {
|
||||
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
|
||||
sslContextFactory.setKeyStorePassword(HTTP_ENCRYPTION.keystorePassword);
|
||||
} catch (Exception ignored) {
|
||||
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error"));
|
||||
|
||||
|
||||
try {
|
||||
sslContextFactory.setKeyStorePath(keystoreFile.getPath());
|
||||
sslContextFactory.setKeyStorePassword("123456");
|
||||
|
||||
|
||||
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password"));
|
||||
} catch (Exception exception) {
|
||||
Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error"), exception);
|
||||
@ -88,10 +88,10 @@ public final class HttpServer {
|
||||
serverConnector = new ServerConnector(server, sslContextFactory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
serverConnector.setPort(HTTP_INFO.bindPort);
|
||||
server.setConnectors(new ServerConnector[]{serverConnector});
|
||||
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
@ -112,9 +112,9 @@ public final class HttpServer {
|
||||
public HttpServer addRouter(Class<? extends Router> router, Object... args) {
|
||||
// Get all constructor parameters.
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
for(var argument : args)
|
||||
for (var argument : args)
|
||||
types[args.length - 1] = argument.getClass();
|
||||
|
||||
|
||||
try { // Create a router instance & apply routes.
|
||||
var constructor = router.getDeclaredConstructor(types); // Get the constructor.
|
||||
var routerInstance = constructor.newInstance(args); // Create instance.
|
||||
@ -130,9 +130,9 @@ public final class HttpServer {
|
||||
*/
|
||||
public void start() throws UnsupportedEncodingException {
|
||||
// Attempt to start the HTTP server.
|
||||
if(HTTP_INFO.bindAddress.equals("")){
|
||||
if (HTTP_INFO.bindAddress.equals("")) {
|
||||
this.express.listen(HTTP_INFO.bindPort);
|
||||
}else{
|
||||
}else {
|
||||
this.express.listen(HTTP_INFO.bindAddress, HTTP_INFO.bindPort);
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ public final class HttpServer {
|
||||
@Override public void applyRoutes(Express express, Javalin handle) {
|
||||
express.get("/", (request, response) -> {
|
||||
File file = new File(HTTP_STATIC_FILES.indexFile);
|
||||
if(!file.exists())
|
||||
if (!file.exists())
|
||||
response.send("""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -173,19 +173,19 @@ public final class HttpServer {
|
||||
public static class UnhandledRequestRouter implements Router {
|
||||
@Override public void applyRoutes(Express express, Javalin handle) {
|
||||
handle.error(404, context -> {
|
||||
if(DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
|
||||
if (DISPATCH_INFO.logRequests == ServerDebugMode.MISSING)
|
||||
Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", context.method(), context.url()));
|
||||
context.contentType("text/html");
|
||||
|
||||
|
||||
File file = new File(HTTP_STATIC_FILES.errorFile);
|
||||
if(!file.exists())
|
||||
if (!file.exists())
|
||||
context.result("""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf8">
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<img src="https://http.cat/404" />
|
||||
</body>
|
||||
|
@ -60,7 +60,7 @@ public final class RegionHandler implements Router {
|
||||
List<String> usedNames = new ArrayList<>(); // List to check for potential naming conflicts.
|
||||
|
||||
var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions));
|
||||
if(SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
|
||||
if (SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) {
|
||||
Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
|
||||
System.exit(1);
|
||||
} else if (configuredRegions.size() == 0)
|
||||
@ -136,11 +136,11 @@ public final class RegionHandler implements Router {
|
||||
// Get region data.
|
||||
String regionData = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
|
||||
if (request.query().values().size() > 0) {
|
||||
if(region != null)
|
||||
if (region != null)
|
||||
regionData = region.getBase64();
|
||||
}
|
||||
|
||||
if( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
|
||||
if ( versionName.contains("2.7.5") || versionName.contains("2.8.")) {
|
||||
try {
|
||||
QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call();
|
||||
|
||||
@ -227,4 +227,4 @@ public final class RegionHandler implements Router {
|
||||
public static QueryCurrRegionHttpRsp getCurrentRegion() {
|
||||
return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user