Fix player fields not being set

line seps r weird
This commit is contained in:
KingRainbow44
2023-04-10 22:46:19 -04:00
parent 5e56b5e3a8
commit 06cbae31fa
453 changed files with 63228 additions and 63071 deletions

View File

@@ -1,35 +1,35 @@
package emu.grasscutter;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
import java.util.Arrays;
public final class GameConstants {
public static final int DEFAULT_TEAMS = 4;
public static final int MAX_TEAMS = 10;
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 = 60;
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 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");
public static String VERSION = "3.5.0";
}
package emu.grasscutter;
import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils;
import java.util.Arrays;
public final class GameConstants {
public static final int DEFAULT_TEAMS = 4;
public static final int MAX_TEAMS = 10;
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 = 60;
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 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");
public static String VERSION = "3.5.0";
}

View File

@@ -1,322 +1,322 @@
package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.Language.translate;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.auth.AuthenticationSystem;
import emu.grasscutter.auth.DefaultAuthentication;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.command.DefaultPermissionHandler;
import emu.grasscutter.command.PermissionHandler;
import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHook;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.dispatch.DispatchHandler;
import emu.grasscutter.server.http.dispatch.RegionHandler;
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
import emu.grasscutter.server.http.handlers.AnnouncementsHandler;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.server.http.handlers.GenericHandler;
import emu.grasscutter.server.http.handlers.LogHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOError;
import java.io.IOException;
import java.util.Calendar;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.reflections.Reflections;
import org.slf4j.LoggerFactory;
public final class Grasscutter {
public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter");
@Getter private static final Logger logger = (Logger) LoggerFactory.getLogger(Grasscutter.class);
@Getter public static ConfigContainer config;
@Getter @Setter private static Language language;
@Getter @Setter private static String preferredLanguage;
@Getter private static int currentDayOfWeek;
@Setter private static ServerRunMode runModeOverride = null; // Config override for run mode
@Getter private static HttpServer httpServer;
@Getter private static GameServer gameServer;
@Getter private static PluginManager pluginManager;
@Getter private static CommandMap commandMap;
@Getter @Setter private static AuthenticationSystem authenticationSystem;
@Getter @Setter private static PermissionHandler permissionHandler;
private static LineReader consoleLineReader = null;
static {
// Declare logback configuration.
System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
// Disable the MongoDB logger.
var mongoLogger = (Logger) LoggerFactory.getLogger("org.mongodb.driver");
mongoLogger.setLevel(Level.OFF);
// Load server configuration.
Grasscutter.loadConfig();
// Attempt to update configuration.
ConfigContainer.updateConfig();
// Load translation files.
Grasscutter.loadLanguage();
// Check server structure.
Utils.startupCheck();
}
public static void main(String[] args) throws Exception {
Crypto.loadKeys(); // Load keys from buffers.
// Parse start-up arguments.
if (StartupArguments.parse(args)) {
System.exit(0); // Exit early.
}
// Create command map.
commandMap = new CommandMap(true);
// Initialize server.
logger.info(translate("messages.status.starting"));
logger.info(translate("messages.status.game_version", GameConstants.VERSION));
logger.info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
// Load all resources.
Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll();
ScriptLoader.init();
// Generate handbooks.
Tools.createGmHandbooks(false);
// Initialize database.
DatabaseManager.initialize();
// Initialize the default systems.
authenticationSystem = new DefaultAuthentication();
permissionHandler = new DefaultPermissionHandler();
// Create server instances.
httpServer = new HttpServer();
gameServer = new GameServer();
// Create a server hook instance with both servers.
new ServerHook(gameServer, httpServer);
// Create plugin manager instance.
pluginManager = new PluginManager();
// Add HTTP routes after loading plugins.
httpServer.addRouter(HttpServer.UnhandledRequestRouter.class);
httpServer.addRouter(HttpServer.DefaultRequestRouter.class);
httpServer.addRouter(RegionHandler.class);
httpServer.addRouter(LogHandler.class);
httpServer.addRouter(GenericHandler.class);
httpServer.addRouter(AnnouncementsHandler.class);
httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class);
// Start servers.
var runMode = Grasscutter.getRunMode();
if (runMode == ServerRunMode.HYBRID) {
httpServer.start();
gameServer.start();
} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
httpServer.start();
} else if (runMode == ServerRunMode.GAME_ONLY) {
gameServer.start();
} else {
logger.error(translate("messages.status.run_mode_error", runMode));
logger.error(translate("messages.status.run_mode_help"));
logger.error(translate("messages.status.shutdown"));
System.exit(1);
}
// Enable all plugins.
pluginManager.enablePlugins();
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
// Open console.
Grasscutter.startConsole();
}
/** Server shutdown event. */
private static void onShutdown() {
// Disable all plugins.
if (pluginManager != null) pluginManager.disablePlugins();
}
/*
* Methods for the language system component.
*/
public static void loadLanguage() {
var locale = config.language.language;
language = Language.getLanguage(Utils.getLanguageCode(locale));
}
/*
* Methods for the configuration system component.
*/
/** Attempts to load the configuration from a file. */
public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config.
if (!configFile.exists()) {
getLogger().info("config.json could not be found. Generating a default configuration ...");
config = new ConfigContainer();
Grasscutter.saveConfig(config);
return;
}
// If the file already exists, we attempt to load it.
try {
config = JsonUtils.loadToClass(configFile.toPath(), ConfigContainer.class);
} catch (Exception exception) {
getLogger()
.error(
"There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
System.exit(1);
}
}
/**
* Saves the provided server configuration.
*
* @param config The configuration to save, or null for a new one.
*/
public static void saveConfig(@Nullable ConfigContainer config) {
if (config == null) config = new ConfigContainer();
try (FileWriter file = new FileWriter(configFile)) {
file.write(JsonUtils.encode(config));
} catch (IOException ignored) {
logger.error("Unable to write to config file.");
} catch (Exception e) {
logger.error("Unable to save config file.", e);
}
}
/*
* Getters for the various server components.
*/
public static Language getLanguage(String langCode) {
return Language.getLanguage(langCode);
}
public static ServerRunMode getRunMode() {
return Grasscutter.runModeOverride != null ? Grasscutter.runModeOverride : SERVER.runMode;
}
public static LineReader getConsole() {
if (consoleLineReader == null) {
Terminal terminal = null;
try {
terminal = TerminalBuilder.builder().jna(true).build();
} catch (Exception e) {
try {
// Fallback to a dumb jline terminal.
terminal = TerminalBuilder.builder().dumb(true).build();
} catch (Exception ignored) {
// When dumb is true, build() never throws.
}
}
consoleLineReader = LineReaderBuilder.builder().terminal(terminal).build();
}
return consoleLineReader;
}
/*
* Utility methods.
*/
public static void updateDayOfWeek() {
Calendar calendar = Calendar.getInstance();
Grasscutter.currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
logger.debug("Set day of week to " + currentDayOfWeek);
}
public static void startConsole() {
// Console should not start in dispatch only mode.
if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) {
logger.info(translate("messages.dispatch.no_commands_error"));
return;
} else {
logger.info(translate("messages.status.done"));
}
String input = null;
var isLastInterrupted = false;
while (config.server.game.enableConsole) {
try {
input = consoleLineReader.readLine("> ");
} catch (UserInterruptException e) {
if (!isLastInterrupted) {
isLastInterrupted = true;
logger.info("Press Ctrl-C again to shutdown.");
continue;
} else {
Runtime.getRuntime().exit(0);
}
} catch (EndOfFileException e) {
logger.info("EOF detected.");
continue;
} catch (IOError e) {
logger.error("An IO error occurred while trying to read from console.", e);
return;
}
isLastInterrupted = false;
try {
commandMap.invoke(null, null, input);
} catch (Exception e) {
logger.error(translate("messages.game.command_error"), e);
}
}
}
/*
* Enums for the configuration.
*/
public enum ServerRunMode {
HYBRID,
DISPATCH_ONLY,
GAME_ONLY
}
public enum ServerDebugMode {
ALL,
MISSING,
WHITELIST,
BLACKLIST,
NONE
}
}
package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.Language.translate;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.auth.AuthenticationSystem;
import emu.grasscutter.auth.DefaultAuthentication;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.command.DefaultPermissionHandler;
import emu.grasscutter.command.PermissionHandler;
import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHook;
import emu.grasscutter.scripts.ScriptLoader;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.dispatch.DispatchHandler;
import emu.grasscutter.server.http.dispatch.RegionHandler;
import emu.grasscutter.server.http.documentation.DocumentationServerHandler;
import emu.grasscutter.server.http.handlers.AnnouncementsHandler;
import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.server.http.handlers.GenericHandler;
import emu.grasscutter.server.http.handlers.LogHandler;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*;
import java.io.File;
import java.io.FileWriter;
import java.io.IOError;
import java.io.IOException;
import java.util.Calendar;
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.reflections.Reflections;
import org.slf4j.LoggerFactory;
public final class Grasscutter {
public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter");
@Getter private static final Logger logger = (Logger) LoggerFactory.getLogger(Grasscutter.class);
@Getter public static ConfigContainer config;
@Getter @Setter private static Language language;
@Getter @Setter private static String preferredLanguage;
@Getter private static int currentDayOfWeek;
@Setter private static ServerRunMode runModeOverride = null; // Config override for run mode
@Getter private static HttpServer httpServer;
@Getter private static GameServer gameServer;
@Getter private static PluginManager pluginManager;
@Getter private static CommandMap commandMap;
@Getter @Setter private static AuthenticationSystem authenticationSystem;
@Getter @Setter private static PermissionHandler permissionHandler;
private static LineReader consoleLineReader = null;
static {
// Declare logback configuration.
System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
// Disable the MongoDB logger.
var mongoLogger = (Logger) LoggerFactory.getLogger("org.mongodb.driver");
mongoLogger.setLevel(Level.OFF);
// Load server configuration.
Grasscutter.loadConfig();
// Attempt to update configuration.
ConfigContainer.updateConfig();
// Load translation files.
Grasscutter.loadLanguage();
// Check server structure.
Utils.startupCheck();
}
public static void main(String[] args) throws Exception {
Crypto.loadKeys(); // Load keys from buffers.
// Parse start-up arguments.
if (StartupArguments.parse(args)) {
System.exit(0); // Exit early.
}
// Create command map.
commandMap = new CommandMap(true);
// Initialize server.
logger.info(translate("messages.status.starting"));
logger.info(translate("messages.status.game_version", GameConstants.VERSION));
logger.info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
// Load all resources.
Grasscutter.updateDayOfWeek();
ResourceLoader.loadAll();
ScriptLoader.init();
// Generate handbooks.
Tools.createGmHandbooks(false);
// Initialize database.
DatabaseManager.initialize();
// Initialize the default systems.
authenticationSystem = new DefaultAuthentication();
permissionHandler = new DefaultPermissionHandler();
// Create server instances.
httpServer = new HttpServer();
gameServer = new GameServer();
// Create a server hook instance with both servers.
new ServerHook(gameServer, httpServer);
// Create plugin manager instance.
pluginManager = new PluginManager();
// Add HTTP routes after loading plugins.
httpServer.addRouter(HttpServer.UnhandledRequestRouter.class);
httpServer.addRouter(HttpServer.DefaultRequestRouter.class);
httpServer.addRouter(RegionHandler.class);
httpServer.addRouter(LogHandler.class);
httpServer.addRouter(GenericHandler.class);
httpServer.addRouter(AnnouncementsHandler.class);
httpServer.addRouter(DispatchHandler.class);
httpServer.addRouter(GachaHandler.class);
httpServer.addRouter(DocumentationServerHandler.class);
// Start servers.
var runMode = Grasscutter.getRunMode();
if (runMode == ServerRunMode.HYBRID) {
httpServer.start();
gameServer.start();
} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
httpServer.start();
} else if (runMode == ServerRunMode.GAME_ONLY) {
gameServer.start();
} else {
logger.error(translate("messages.status.run_mode_error", runMode));
logger.error(translate("messages.status.run_mode_help"));
logger.error(translate("messages.status.shutdown"));
System.exit(1);
}
// Enable all plugins.
pluginManager.enablePlugins();
// Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
// Open console.
Grasscutter.startConsole();
}
/** Server shutdown event. */
private static void onShutdown() {
// Disable all plugins.
if (pluginManager != null) pluginManager.disablePlugins();
}
/*
* Methods for the language system component.
*/
public static void loadLanguage() {
var locale = config.language.language;
language = Language.getLanguage(Utils.getLanguageCode(locale));
}
/*
* Methods for the configuration system component.
*/
/** Attempts to load the configuration from a file. */
public static void loadConfig() {
// Check if config.json exists. If not, we generate a new config.
if (!configFile.exists()) {
getLogger().info("config.json could not be found. Generating a default configuration ...");
config = new ConfigContainer();
Grasscutter.saveConfig(config);
return;
}
// If the file already exists, we attempt to load it.
try {
config = JsonUtils.loadToClass(configFile.toPath(), ConfigContainer.class);
} catch (Exception exception) {
getLogger()
.error(
"There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json.");
System.exit(1);
}
}
/**
* Saves the provided server configuration.
*
* @param config The configuration to save, or null for a new one.
*/
public static void saveConfig(@Nullable ConfigContainer config) {
if (config == null) config = new ConfigContainer();
try (FileWriter file = new FileWriter(configFile)) {
file.write(JsonUtils.encode(config));
} catch (IOException ignored) {
logger.error("Unable to write to config file.");
} catch (Exception e) {
logger.error("Unable to save config file.", e);
}
}
/*
* Getters for the various server components.
*/
public static Language getLanguage(String langCode) {
return Language.getLanguage(langCode);
}
public static ServerRunMode getRunMode() {
return Grasscutter.runModeOverride != null ? Grasscutter.runModeOverride : SERVER.runMode;
}
public static LineReader getConsole() {
if (consoleLineReader == null) {
Terminal terminal = null;
try {
terminal = TerminalBuilder.builder().jna(true).build();
} catch (Exception e) {
try {
// Fallback to a dumb jline terminal.
terminal = TerminalBuilder.builder().dumb(true).build();
} catch (Exception ignored) {
// When dumb is true, build() never throws.
}
}
consoleLineReader = LineReaderBuilder.builder().terminal(terminal).build();
}
return consoleLineReader;
}
/*
* Utility methods.
*/
public static void updateDayOfWeek() {
Calendar calendar = Calendar.getInstance();
Grasscutter.currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
logger.debug("Set day of week to " + currentDayOfWeek);
}
public static void startConsole() {
// Console should not start in dispatch only mode.
if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) {
logger.info(translate("messages.dispatch.no_commands_error"));
return;
} else {
logger.info(translate("messages.status.done"));
}
String input = null;
var isLastInterrupted = false;
while (config.server.game.enableConsole) {
try {
input = consoleLineReader.readLine("> ");
} catch (UserInterruptException e) {
if (!isLastInterrupted) {
isLastInterrupted = true;
logger.info("Press Ctrl-C again to shutdown.");
continue;
} else {
Runtime.getRuntime().exit(0);
}
} catch (EndOfFileException e) {
logger.info("EOF detected.");
continue;
} catch (IOError e) {
logger.error("An IO error occurred while trying to read from console.", e);
return;
}
isLastInterrupted = false;
try {
commandMap.invoke(null, null, input);
} catch (Exception e) {
logger.error(translate("messages.game.command_error"), e);
}
}
}
/*
* Enums for the configuration.
*/
public enum ServerRunMode {
HYBRID,
DISPATCH_ONLY,
GAME_ONLY
}
public enum ServerDebugMode {
ALL,
MISSING,
WHITELIST,
BLACKLIST,
NONE
}
}

View File

@@ -1,158 +1,158 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.achievement.AchievementData;
import emu.grasscutter.game.achievement.AchievementControlReturns;
import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.player.Player;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@Command(
label = "achievement",
usage = {
"(grant|revoke) <achievementId>",
"progress <achievementId> <progress>",
"grantall",
"revokeall"
},
aliases = {"am"},
permission = "player.achievement",
permissionTargeted = "player.achievement.others",
targetRequirement = Command.TargetRequirement.PLAYER,
threading = true)
public final class AchievementCommand implements CommandHandler {
private static void sendSuccessMessage(Player sender, String cmd, Object... args) {
CommandHandler.sendTranslatedMessage(
sender, AchievementControlReturns.Return.SUCCESS.getKey() + cmd, args);
}
private static Optional<Integer> parseInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
private static void grantAll(Player sender, Player targetPlayer, Achievements achievements) {
var counter = new AtomicInteger();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.filter(AchievementData::isParent)
.forEach(
data -> {
var success = achievements.grant(data.getId());
if (success.getRet() == AchievementControlReturns.Return.SUCCESS) {
counter.addAndGet(success.getChangedAchievementStatusNum());
}
});
sendSuccessMessage(sender, "grantall", counter.intValue(), targetPlayer.getNickname());
}
private static void revokeAll(Player sender, Player targetPlayer, Achievements achievements) {
var counter = new AtomicInteger();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.filter(AchievementData::isParent)
.forEach(
data -> {
var success = achievements.revoke(data.getId());
if (success.getRet() == AchievementControlReturns.Return.SUCCESS) {
counter.addAndGet(success.getChangedAchievementStatusNum());
}
});
sendSuccessMessage(sender, "revokeall", counter.intValue(), targetPlayer.getNickname());
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
return;
}
var command = args.remove(0).toLowerCase();
var achievements = Achievements.getByPlayer(targetPlayer);
switch (command) {
case "grant" -> this.grant(sender, targetPlayer, achievements, args);
case "revoke" -> this.revoke(sender, targetPlayer, achievements, args);
case "progress" -> this.progress(sender, targetPlayer, achievements, args);
case "grantall" -> grantAll(sender, targetPlayer, achievements);
case "revokeall" -> revokeAll(sender, targetPlayer, achievements);
default -> this.sendUsageMessage(sender);
}
}
private void grant(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
var ret = achievements.grant(integer);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(sender, "grant", targetPlayer.getNickname());
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
case ALREADY_ACHIEVED -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey(), targetPlayer.getNickname());
}
},
() -> this.sendUsageMessage(sender));
}
private void revoke(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
var ret = achievements.revoke(integer);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(sender, "revoke", targetPlayer.getNickname());
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
case NOT_YET_ACHIEVED -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey(), targetPlayer.getNickname());
}
},
() -> this.sendUsageMessage(sender));
}
private void progress(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 2) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
parseInt(args.remove(0))
.ifPresentOrElse(
progress -> {
var ret = achievements.progress(integer, progress);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(
sender, "progress", targetPlayer.getNickname(), integer, progress);
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
}
},
() -> this.sendUsageMessage(sender));
},
() -> this.sendUsageMessage(sender));
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.achievement.AchievementData;
import emu.grasscutter.game.achievement.AchievementControlReturns;
import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.player.Player;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@Command(
label = "achievement",
usage = {
"(grant|revoke) <achievementId>",
"progress <achievementId> <progress>",
"grantall",
"revokeall"
},
aliases = {"am"},
permission = "player.achievement",
permissionTargeted = "player.achievement.others",
targetRequirement = Command.TargetRequirement.PLAYER,
threading = true)
public final class AchievementCommand implements CommandHandler {
private static void sendSuccessMessage(Player sender, String cmd, Object... args) {
CommandHandler.sendTranslatedMessage(
sender, AchievementControlReturns.Return.SUCCESS.getKey() + cmd, args);
}
private static Optional<Integer> parseInt(String s) {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.empty();
}
}
private static void grantAll(Player sender, Player targetPlayer, Achievements achievements) {
var counter = new AtomicInteger();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.filter(AchievementData::isParent)
.forEach(
data -> {
var success = achievements.grant(data.getId());
if (success.getRet() == AchievementControlReturns.Return.SUCCESS) {
counter.addAndGet(success.getChangedAchievementStatusNum());
}
});
sendSuccessMessage(sender, "grantall", counter.intValue(), targetPlayer.getNickname());
}
private static void revokeAll(Player sender, Player targetPlayer, Achievements achievements) {
var counter = new AtomicInteger();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.filter(AchievementData::isParent)
.forEach(
data -> {
var success = achievements.revoke(data.getId());
if (success.getRet() == AchievementControlReturns.Return.SUCCESS) {
counter.addAndGet(success.getChangedAchievementStatusNum());
}
});
sendSuccessMessage(sender, "revokeall", counter.intValue(), targetPlayer.getNickname());
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
return;
}
var command = args.remove(0).toLowerCase();
var achievements = Achievements.getByPlayer(targetPlayer);
switch (command) {
case "grant" -> this.grant(sender, targetPlayer, achievements, args);
case "revoke" -> this.revoke(sender, targetPlayer, achievements, args);
case "progress" -> this.progress(sender, targetPlayer, achievements, args);
case "grantall" -> grantAll(sender, targetPlayer, achievements);
case "revokeall" -> revokeAll(sender, targetPlayer, achievements);
default -> this.sendUsageMessage(sender);
}
}
private void grant(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
var ret = achievements.grant(integer);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(sender, "grant", targetPlayer.getNickname());
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
case ALREADY_ACHIEVED -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey(), targetPlayer.getNickname());
}
},
() -> this.sendUsageMessage(sender));
}
private void revoke(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 1) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
var ret = achievements.revoke(integer);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(sender, "revoke", targetPlayer.getNickname());
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
case NOT_YET_ACHIEVED -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey(), targetPlayer.getNickname());
}
},
() -> this.sendUsageMessage(sender));
}
private void progress(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.size() < 2) {
this.sendUsageMessage(sender);
}
parseInt(args.remove(0))
.ifPresentOrElse(
integer -> {
parseInt(args.remove(0))
.ifPresentOrElse(
progress -> {
var ret = achievements.progress(integer, progress);
switch (ret.getRet()) {
case SUCCESS -> sendSuccessMessage(
sender, "progress", targetPlayer.getNickname(), integer, progress);
case ACHIEVEMENT_NOT_FOUND -> CommandHandler.sendTranslatedMessage(
sender, ret.getRet().getKey());
}
},
() -> this.sendUsageMessage(sender));
},
() -> this.sendUsageMessage(sender));
}
}

View File

@@ -1,139 +1,139 @@
package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import lombok.Setter;
@Command(
label = "entity",
usage = {
"<configId gadget> [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]",
"<configId monster> [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]"
},
permission = "server.entity")
public final class EntityCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<EntityParameters, Integer>> intCommandHandlers =
Map.ofEntries(
Map.entry(stateRegex, EntityParameters::setState),
Map.entry(maxHPRegex, EntityParameters::setMaxHP),
Map.entry(hpRegex, EntityParameters::setHp),
Map.entry(defRegex, EntityParameters::setDef),
Map.entry(atkRegex, EntityParameters::setAtk),
Map.entry(aiRegex, EntityParameters::setAi));
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
EntityParameters param = new EntityParameters();
parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos
if (args.size() != 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException();
}
try {
param.configId = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.cfgId"));
}
param.scene = targetPlayer.getScene();
var entity = param.scene.getEntityByConfigId(param.configId);
if (entity == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error"));
return;
}
applyFightProps(entity, param);
applyGadgetParams(entity, param);
applyMonsterParams(entity, param);
CommandHandler.sendMessage(sender, translate(sender, "commands.status.success"));
}
private void applyGadgetParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityGadget)) {
return;
}
if (param.state != -1) {
((EntityGadget) entity).updateState(param.state);
}
}
private void applyMonsterParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityMonster)) {
return;
}
if (param.ai != -1) {
((EntityMonster) entity).setAiId(param.ai);
// TODO notify
}
}
private void applyFightProps(GameEntity entity, EntityParameters param) {
var changedFields = new ArrayList<FightProperty>();
if (param.maxHP != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_MAX_HP, param.maxHP, changedFields);
}
if (param.hp != -1) {
float targetHp = param.hp == 0 ? Float.MAX_VALUE : param.hp;
float oldHp = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_HP, targetHp, changedFields);
EntityDamageEvent event =
new EntityDamageEvent(entity, oldHp - targetHp, ElementType.None, null);
callHPEvents(entity, event);
}
if (param.atk != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_ATTACK, param.atk, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk, changedFields);
}
if (param.def != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_DEFENSE, param.def, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def, changedFields);
}
if (!changedFields.isEmpty()) {
entity
.getScene()
.broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, changedFields));
}
}
private void callHPEvents(GameEntity entity, EntityDamageEvent event) {
entity.runLuaCallbacks(event);
}
private void setFightProperty(
GameEntity entity, FightProperty property, float value, List<FightProperty> modifiedProps) {
entity.setFightProperty(property, value);
modifiedProps.add(property);
}
private static class EntityParameters {
@Setter public int configId = -1;
@Setter public int state = -1;
@Setter public int hp = -1;
@Setter public int maxHP = -1;
@Setter public int atk = -1;
@Setter public int def = -1;
@Setter public int ai = -1;
public Scene scene = null;
}
}
package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.event.entity.EntityDamageEvent;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import lombok.Setter;
@Command(
label = "entity",
usage = {
"<configId gadget> [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]",
"<configId monster> [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>]"
},
permission = "server.entity")
public final class EntityCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<EntityParameters, Integer>> intCommandHandlers =
Map.ofEntries(
Map.entry(stateRegex, EntityParameters::setState),
Map.entry(maxHPRegex, EntityParameters::setMaxHP),
Map.entry(hpRegex, EntityParameters::setHp),
Map.entry(defRegex, EntityParameters::setDef),
Map.entry(atkRegex, EntityParameters::setAtk),
Map.entry(aiRegex, EntityParameters::setAi));
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
EntityParameters param = new EntityParameters();
parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos
if (args.size() != 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException();
}
try {
param.configId = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.cfgId"));
}
param.scene = targetPlayer.getScene();
var entity = param.scene.getEntityByConfigId(param.configId);
if (entity == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error"));
return;
}
applyFightProps(entity, param);
applyGadgetParams(entity, param);
applyMonsterParams(entity, param);
CommandHandler.sendMessage(sender, translate(sender, "commands.status.success"));
}
private void applyGadgetParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityGadget)) {
return;
}
if (param.state != -1) {
((EntityGadget) entity).updateState(param.state);
}
}
private void applyMonsterParams(GameEntity entity, EntityParameters param) {
if (!(entity instanceof EntityMonster)) {
return;
}
if (param.ai != -1) {
((EntityMonster) entity).setAiId(param.ai);
// TODO notify
}
}
private void applyFightProps(GameEntity entity, EntityParameters param) {
var changedFields = new ArrayList<FightProperty>();
if (param.maxHP != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_MAX_HP, param.maxHP, changedFields);
}
if (param.hp != -1) {
float targetHp = param.hp == 0 ? Float.MAX_VALUE : param.hp;
float oldHp = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_HP, targetHp, changedFields);
EntityDamageEvent event =
new EntityDamageEvent(entity, oldHp - targetHp, ElementType.None, null);
callHPEvents(entity, event);
}
if (param.atk != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_ATTACK, param.atk, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk, changedFields);
}
if (param.def != -1) {
setFightProperty(entity, FightProperty.FIGHT_PROP_DEFENSE, param.def, changedFields);
setFightProperty(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def, changedFields);
}
if (!changedFields.isEmpty()) {
entity
.getScene()
.broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, changedFields));
}
}
private void callHPEvents(GameEntity entity, EntityDamageEvent event) {
entity.runLuaCallbacks(event);
}
private void setFightProperty(
GameEntity entity, FightProperty property, float value, List<FightProperty> modifiedProps) {
entity.setFightProperty(property, value);
modifiedProps.add(property);
}
private static class EntityParameters {
@Setter public int configId = -1;
@Setter public int state = -1;
@Setter public int hp = -1;
@Setter public int maxHP = -1;
@Setter public int atk = -1;
@Setter public int def = -1;
@Setter public int ai = -1;
public Scene scene = null;
}
}

View File

@@ -1,72 +1,72 @@
package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List;
@Command(
label = "group",
aliases = {"g"},
usage = {"(refresh) [<groupId>] [<suiteId>]"},
permission = "player.group",
permissionTargeted = "player.group.others")
public final class GroupCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
return;
}
String cmd = args.remove(0).toLowerCase();
int groupId = 0;
int suiteId = 0;
switch (args.size()) {
case 2:
try {
suiteId = Integer.parseInt(args.get(1));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_suiteid"));
return;
} // Fallthrough
case 1:
try {
groupId = Integer.parseInt(args.get(0));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_groupid"));
return;
}
break;
default:
sendUsageMessage(sender);
return;
}
switch (cmd) {
case "refresh" -> {
SceneGroupInstance groupInstance =
targetPlayer.getScene().getScriptManager().getGroupInstanceById(groupId);
if (groupInstance == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.group.group_not_found", groupId));
return;
}
if (args.size() >= 2) {
targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance, suiteId, false);
} else {
targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance);
}
CommandHandler.sendMessage(sender, translate(sender, "commands.group.refreshed", groupId));
}
default -> {
sendUsageMessage(sender);
}
}
}
}
package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List;
@Command(
label = "group",
aliases = {"g"},
usage = {"(refresh) [<groupId>] [<suiteId>]"},
permission = "player.group",
permissionTargeted = "player.group.others")
public final class GroupCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
return;
}
String cmd = args.remove(0).toLowerCase();
int groupId = 0;
int suiteId = 0;
switch (args.size()) {
case 2:
try {
suiteId = Integer.parseInt(args.get(1));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_suiteid"));
return;
} // Fallthrough
case 1:
try {
groupId = Integer.parseInt(args.get(0));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.group.invalid_groupid"));
return;
}
break;
default:
sendUsageMessage(sender);
return;
}
switch (cmd) {
case "refresh" -> {
SceneGroupInstance groupInstance =
targetPlayer.getScene().getScriptManager().getGroupInstanceById(groupId);
if (groupInstance == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.group.group_not_found", groupId));
return;
}
if (args.size() >= 2) {
targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance, suiteId, false);
} else {
targetPlayer.getScene().getScriptManager().refreshGroup(groupInstance);
}
CommandHandler.sendMessage(sender, translate(sender, "commands.group.refreshed", groupId));
}
default -> {
sendUsageMessage(sender);
}
}
}
}

View File

@@ -1,279 +1,279 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify;
import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Command(
label = "setProp",
aliases = {"prop"},
usage = {"<prop> <value>"},
permission = "player.setprop",
permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler {
// List of map areas. Unfortunately, there is no readily available source for them in excels or
// bins.
private static final List<Integer> sceneAreas =
List.of(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 32, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403);
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("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA);
this.props.put("unlimitedstamina", nostamina);
this.props.put("us", nostamina);
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);
Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE);
this.props.put("setopenstate", setopenstate);
this.props.put("so", setopenstate);
Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE);
this.props.put("unsetopenstate", unsetopenstate);
this.props.put("uo", unsetopenstate);
Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP);
this.props.put("unlockmap", unlockmap);
this.props.put("um", unlockmap);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
sendUsageMessage(sender);
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
sendUsageMessage(sender);
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, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool(
sender, targetPlayer, prop.pseudoProp, value);
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer);
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
int min = targetPlayer.getPropertyMin(prop.prop);
int max = 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()) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size());
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())) {
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.isInGodMode();
case UNLIMITED_STAMINA -> targetPlayer.isUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().isEnergyUsage();
default -> false;
};
enabled =
switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setInGodMode(enabled);
break;
case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
private boolean setOpenState(Player targetPlayer, int state, int value) {
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value));
return true;
}
private boolean unlockMap(Player targetPlayer) {
// Unlock.
GameData.getScenePointsPerScene()
.forEach(
(sceneId, scenePoints) -> {
// Unlock trans points.
targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints);
// Unlock map areas.
targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas);
});
// Send notify.
int playerScene = targetPlayer.getSceneId();
targetPlayer.sendPacket(
new PacketScenePointUnlockNotify(
playerScene, targetPlayer.getUnlockedScenePoints(playerScene)));
targetPlayer.sendPacket(
new PacketSceneAreaUnlockNotify(
playerScene, targetPlayer.getUnlockedSceneAreas(playerScene)));
return true;
}
enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
UNLIMITED_STAMINA,
UNLIMITED_ENERGY,
SET_OPENSTATE,
UNSET_OPENSTATE,
UNLOCK_MAP
}
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;
}
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import emu.grasscutter.server.packet.send.PacketSceneAreaUnlockNotify;
import emu.grasscutter.server.packet.send.PacketScenePointUnlockNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Command(
label = "setProp",
aliases = {"prop"},
usage = {"<prop> <value>"},
permission = "player.setprop",
permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler {
// List of map areas. Unfortunately, there is no readily available source for them in excels or
// bins.
private static final List<Integer> sceneAreas =
List.of(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 32, 100, 101, 102, 103, 200, 210, 300, 400, 401, 402, 403);
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("UnlimitedStamina", PseudoProp.UNLIMITED_STAMINA);
this.props.put("unlimitedstamina", nostamina);
this.props.put("us", nostamina);
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);
Prop setopenstate = new Prop("SetOpenstate", PseudoProp.SET_OPENSTATE);
this.props.put("setopenstate", setopenstate);
this.props.put("so", setopenstate);
Prop unsetopenstate = new Prop("UnsetOpenstate", PseudoProp.UNSET_OPENSTATE);
this.props.put("unsetopenstate", unsetopenstate);
this.props.put("uo", unsetopenstate);
Prop unlockmap = new Prop("UnlockMap", PseudoProp.UNLOCK_MAP);
this.props.put("unlockmap", unlockmap);
this.props.put("um", unlockmap);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
sendUsageMessage(sender);
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
sendUsageMessage(sender);
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, UNLIMITED_STAMINA, UNLIMITED_ENERGY -> this.setBool(
sender, targetPlayer, prop.pseudoProp, value);
case SET_OPENSTATE -> this.setOpenState(targetPlayer, value, 1);
case UNSET_OPENSTATE -> this.setOpenState(targetPlayer, value, 0);
case UNLOCK_MAP -> unlockMap(targetPlayer);
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
int min = targetPlayer.getPropertyMin(prop.prop);
int max = 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()) {
CommandHandler.sendTranslatedMessage(
sender, "commands.generic.invalid.value_between", "Tower Level", 0, floorIds.size());
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())) {
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.isInGodMode();
case UNLIMITED_STAMINA -> targetPlayer.isUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().isEnergyUsage();
default -> false;
};
enabled =
switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setInGodMode(enabled);
break;
case UNLIMITED_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
private boolean setOpenState(Player targetPlayer, int state, int value) {
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(state, value));
return true;
}
private boolean unlockMap(Player targetPlayer) {
// Unlock.
GameData.getScenePointsPerScene()
.forEach(
(sceneId, scenePoints) -> {
// Unlock trans points.
targetPlayer.getUnlockedScenePoints(sceneId).addAll(scenePoints);
// Unlock map areas.
targetPlayer.getUnlockedSceneAreas(sceneId).addAll(sceneAreas);
});
// Send notify.
int playerScene = targetPlayer.getSceneId();
targetPlayer.sendPacket(
new PacketScenePointUnlockNotify(
playerScene, targetPlayer.getUnlockedScenePoints(playerScene)));
targetPlayer.sendPacket(
new PacketSceneAreaUnlockNotify(
playerScene, targetPlayer.getUnlockedSceneAreas(playerScene)));
return true;
}
enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
UNLIMITED_STAMINA,
UNLIMITED_ENERGY,
SET_OPENSTATE,
UNSET_OPENSTATE,
UNLOCK_MAP
}
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;
}
}
}

View File

@@ -1,217 +1,217 @@
package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import lombok.Setter;
@Command(
label = "spawn",
aliases = {"drop", "s"},
usage = {
"<itemId> [x<amount>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<gadgetId> [x<amount>] [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<monsterId> [x<amount>] [lv<level>] [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>"
},
permission = "server.spawn",
permissionTargeted = "server.spawn.others")
public final class SpawnCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<SpawnParameters, Integer>> intCommandHandlers =
Map.ofEntries(
Map.entry(lvlRegex, SpawnParameters::setLvl),
Map.entry(amountRegex, SpawnParameters::setAmount),
Map.entry(stateRegex, SpawnParameters::setState),
Map.entry(blockRegex, SpawnParameters::setBlockId),
Map.entry(groupRegex, SpawnParameters::setGroupId),
Map.entry(configRegex, SpawnParameters::setConfigId),
Map.entry(maxHPRegex, SpawnParameters::setMaxHP),
Map.entry(hpRegex, SpawnParameters::setHp),
Map.entry(defRegex, SpawnParameters::setDef),
Map.entry(atkRegex, SpawnParameters::setAtk),
Map.entry(aiRegex, SpawnParameters::setAi));
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
SpawnParameters param = new SpawnParameters();
parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos
if (args.size() < 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException();
}
switch (args.size()) {
case 4:
try {
float x, y, z;
x = Float.parseFloat(args.get(1));
y = Float.parseFloat(args.get(2));
z = Float.parseFloat(args.get(3));
param.pos = new Position(x, y, z);
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.execution.argument_error"));
} // Fallthrough
case 1:
try {
param.id = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.entityId"));
}
break;
default:
sendUsageMessage(sender);
return;
}
MonsterData monsterData = GameData.getMonsterDataMap().get(param.id);
GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id);
ItemData itemData = GameData.getItemDataMap().get(param.id);
if (monsterData == null && gadgetData == null && itemData == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId"));
return;
}
param.scene = targetPlayer.getScene();
if (param.scene.getEntities().size() + param.amount > GAME_OPTIONS.sceneEntityLimit) {
param.amount =
Math.max(
Math.min(
GAME_OPTIONS.sceneEntityLimit - param.scene.getEntities().size(), param.amount),
0);
CommandHandler.sendMessage(
sender, translate(sender, "commands.spawn.limit_reached", param.amount));
if (param.amount <= 0) {
return;
}
}
double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI);
if (param.pos == null) {
param.pos = targetPlayer.getPosition();
}
for (int i = 0; i < param.amount; i++) {
Position pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3);
GameEntity entity = null;
if (itemData != null) {
entity = createItem(itemData, param, pos);
}
if (gadgetData != null) {
pos.addY(-3);
entity = createGadget(gadgetData, param, pos, targetPlayer);
}
if (monsterData != null) {
entity = createMonster(monsterData, param, pos);
}
applyCommonParameters(entity, param);
param.scene.addEntity(entity);
}
CommandHandler.sendMessage(
sender, translate(sender, "commands.spawn.success", param.amount, param.id));
}
private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) {
return new EntityItem(param.scene, null, itemData, pos, 1, true);
}
private EntityMonster createMonster(
MonsterData monsterData, SpawnParameters param, Position pos) {
var entity = new EntityMonster(param.scene, monsterData, pos, param.lvl);
if (param.ai != -1) {
entity.setAiId(param.ai);
}
return entity;
}
private EntityBaseGadget createGadget(
GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) {
EntityBaseGadget entity;
if (gadgetData.getType() == EntityType.Vehicle) {
entity =
new EntityVehicle(
param.scene, targetPlayer, param.id, 0, pos, targetPlayer.getRotation());
} else {
entity = new EntityGadget(param.scene, param.id, pos, targetPlayer.getRotation());
if (param.state != -1) {
((EntityGadget) entity).setState(param.state);
}
}
return entity;
}
private void applyCommonParameters(GameEntity entity, SpawnParameters param) {
if (param.blockId != -1) {
entity.setBlockId(param.blockId);
}
if (param.groupId != -1) {
entity.setGroupId(param.groupId);
}
if (param.configId != -1) {
entity.setConfigId(param.configId);
}
if (param.maxHP != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, param.maxHP);
entity.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, param.maxHP);
}
if (param.hp != -1) {
entity.setFightProperty(
FightProperty.FIGHT_PROP_CUR_HP, param.hp == 0 ? Float.MAX_VALUE : param.hp);
}
if (param.atk != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_ATTACK, param.atk);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk);
}
if (param.def != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_DEFENSE, param.def);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def);
}
}
private Position GetRandomPositionInCircle(Position origin, double radius) {
Position target = origin.clone();
double angle = Math.random() * 360;
double r = Math.sqrt(Math.random() * radius * radius);
target.addX((float) (r * Math.cos(angle))).addZ((float) (r * Math.sin(angle)));
return target;
}
private static class SpawnParameters {
@Setter public int id;
@Setter public int lvl = 1;
@Setter public int amount = 1;
@Setter public int blockId = -1;
@Setter public int groupId = -1;
@Setter public int configId = -1;
@Setter public int state = -1;
@Setter public int hp = -1;
@Setter public int maxHP = -1;
@Setter public int atk = -1;
@Setter public int def = -1;
@Setter public int ai = -1;
@Setter public Position pos = null;
public Scene scene = null;
}
}
package emu.grasscutter.command.commands;
import static emu.grasscutter.command.CommandHelpers.*;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.monster.MonsterData;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import lombok.Setter;
@Command(
label = "spawn",
aliases = {"drop", "s"},
usage = {
"<itemId> [x<amount>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<gadgetId> [x<amount>] [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<monsterId> [x<amount>] [lv<level>] [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>"
},
permission = "server.spawn",
permissionTargeted = "server.spawn.others")
public final class SpawnCommand implements CommandHandler {
private static final Map<Pattern, BiConsumer<SpawnParameters, Integer>> intCommandHandlers =
Map.ofEntries(
Map.entry(lvlRegex, SpawnParameters::setLvl),
Map.entry(amountRegex, SpawnParameters::setAmount),
Map.entry(stateRegex, SpawnParameters::setState),
Map.entry(blockRegex, SpawnParameters::setBlockId),
Map.entry(groupRegex, SpawnParameters::setGroupId),
Map.entry(configRegex, SpawnParameters::setConfigId),
Map.entry(maxHPRegex, SpawnParameters::setMaxHP),
Map.entry(hpRegex, SpawnParameters::setHp),
Map.entry(defRegex, SpawnParameters::setDef),
Map.entry(atkRegex, SpawnParameters::setAtk),
Map.entry(aiRegex, SpawnParameters::setAi));
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
SpawnParameters param = new SpawnParameters();
parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos
if (args.size() < 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException();
}
switch (args.size()) {
case 4:
try {
float x, y, z;
x = Float.parseFloat(args.get(1));
y = Float.parseFloat(args.get(2));
z = Float.parseFloat(args.get(3));
param.pos = new Position(x, y, z);
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.execution.argument_error"));
} // Fallthrough
case 1:
try {
param.id = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.generic.invalid.entityId"));
}
break;
default:
sendUsageMessage(sender);
return;
}
MonsterData monsterData = GameData.getMonsterDataMap().get(param.id);
GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id);
ItemData itemData = GameData.getItemDataMap().get(param.id);
if (monsterData == null && gadgetData == null && itemData == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId"));
return;
}
param.scene = targetPlayer.getScene();
if (param.scene.getEntities().size() + param.amount > GAME_OPTIONS.sceneEntityLimit) {
param.amount =
Math.max(
Math.min(
GAME_OPTIONS.sceneEntityLimit - param.scene.getEntities().size(), param.amount),
0);
CommandHandler.sendMessage(
sender, translate(sender, "commands.spawn.limit_reached", param.amount));
if (param.amount <= 0) {
return;
}
}
double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI);
if (param.pos == null) {
param.pos = targetPlayer.getPosition();
}
for (int i = 0; i < param.amount; i++) {
Position pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3);
GameEntity entity = null;
if (itemData != null) {
entity = createItem(itemData, param, pos);
}
if (gadgetData != null) {
pos.addY(-3);
entity = createGadget(gadgetData, param, pos, targetPlayer);
}
if (monsterData != null) {
entity = createMonster(monsterData, param, pos);
}
applyCommonParameters(entity, param);
param.scene.addEntity(entity);
}
CommandHandler.sendMessage(
sender, translate(sender, "commands.spawn.success", param.amount, param.id));
}
private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) {
return new EntityItem(param.scene, null, itemData, pos, 1, true);
}
private EntityMonster createMonster(
MonsterData monsterData, SpawnParameters param, Position pos) {
var entity = new EntityMonster(param.scene, monsterData, pos, param.lvl);
if (param.ai != -1) {
entity.setAiId(param.ai);
}
return entity;
}
private EntityBaseGadget createGadget(
GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) {
EntityBaseGadget entity;
if (gadgetData.getType() == EntityType.Vehicle) {
entity =
new EntityVehicle(
param.scene, targetPlayer, param.id, 0, pos, targetPlayer.getRotation());
} else {
entity = new EntityGadget(param.scene, param.id, pos, targetPlayer.getRotation());
if (param.state != -1) {
((EntityGadget) entity).setState(param.state);
}
}
return entity;
}
private void applyCommonParameters(GameEntity entity, SpawnParameters param) {
if (param.blockId != -1) {
entity.setBlockId(param.blockId);
}
if (param.groupId != -1) {
entity.setGroupId(param.groupId);
}
if (param.configId != -1) {
entity.setConfigId(param.configId);
}
if (param.maxHP != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_MAX_HP, param.maxHP);
entity.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, param.maxHP);
}
if (param.hp != -1) {
entity.setFightProperty(
FightProperty.FIGHT_PROP_CUR_HP, param.hp == 0 ? Float.MAX_VALUE : param.hp);
}
if (param.atk != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_ATTACK, param.atk);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, param.atk);
}
if (param.def != -1) {
entity.setFightProperty(FightProperty.FIGHT_PROP_DEFENSE, param.def);
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, param.def);
}
}
private Position GetRandomPositionInCircle(Position origin, double radius) {
Position target = origin.clone();
double angle = Math.random() * 360;
double r = Math.sqrt(Math.random() * radius * radius);
target.addX((float) (r * Math.cos(angle))).addZ((float) (r * Math.sin(angle)));
return target;
}
private static class SpawnParameters {
@Setter public int id;
@Setter public int lvl = 1;
@Setter public int amount = 1;
@Setter public int blockId = -1;
@Setter public int groupId = -1;
@Setter public int configId = -1;
@Setter public int state = -1;
@Setter public int hp = -1;
@Setter public int maxHP = -1;
@Setter public int atk = -1;
@Setter public int def = -1;
@Setter public int ai = -1;
@Setter public Position pos = null;
public Scene scene = null;
}
}

View File

@@ -1,129 +1,129 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Language;
import java.util.List;
@Command(
label = "talent",
usage = {"set <talentId> <level>", "(n|e|q|all) <level>", "getid"},
permission = "player.settalent",
permissionTargeted = "player.settalent.others")
public final class TalentCommand implements CommandHandler {
private void setTalentLevel(Player sender, Avatar avatar, int skillId, int newLevel) {
if (avatar.setSkillLevel(skillId, newLevel)) {
long nameHash = GameData.getAvatarSkillDataMap().get(skillId).getNameTextMapHash();
var name = Language.getTextMapKey(nameHash);
CommandHandler.sendTranslatedMessage(
sender, "commands.talent.set_id", skillId, name, newLevel);
} else {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.out_of_range");
}
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
sendUsageMessage(sender);
return;
}
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
AvatarSkillDepotData skillDepot = avatar.getSkillDepot();
if (skillDepot
== null) { // Avatars without skill depots aren't a suitable target even with manual skillId
// specified
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id");
return;
}
int skillId = 0;
int newLevel = -1;
String cmdSwitch = args.get(0).toLowerCase();
switch (cmdSwitch) {
default -> {
sendUsageMessage(sender);
}
case "set" -> {
if (args.size() < 3) {
sendUsageMessage(sender);
return;
}
try {
skillId = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id");
return;
}
try {
newLevel = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
setTalentLevel(sender, avatar, skillId, newLevel);
}
case "n", "e", "q" -> {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
try {
newLevel = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
skillId =
switch (cmdSwitch) {
default -> skillDepot.getSkills().get(0);
case "e" -> skillDepot.getSkills().get(1);
case "q" -> skillDepot.getEnergySkill();
};
setTalentLevel(sender, avatar, skillId, newLevel);
}
case "all" -> {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
try {
newLevel = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
// This stops setTalentLevel from outputting 3 "levels out of range" messages
if (newLevel < 1 || newLevel > 15) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.out_of_range");
return;
}
int finalNewLevel = newLevel;
skillDepot
.getSkillsAndEnergySkill()
.forEach(id -> setTalentLevel(sender, avatar, id, finalNewLevel));
}
case "getid" -> {
var map = GameData.getAvatarSkillDataMap();
skillDepot
.getSkillsAndEnergySkill()
.forEach(
id -> {
var talent = map.get(id);
if (talent == null) return;
var talentName = Language.getTextMapKey(talent.getNameTextMapHash());
var talentDesc = Language.getTextMapKey(talent.getDescTextMapHash());
CommandHandler.sendTranslatedMessage(
sender, "commands.talent.id_desc", id, talentName, talentDesc);
});
}
}
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Language;
import java.util.List;
@Command(
label = "talent",
usage = {"set <talentId> <level>", "(n|e|q|all) <level>", "getid"},
permission = "player.settalent",
permissionTargeted = "player.settalent.others")
public final class TalentCommand implements CommandHandler {
private void setTalentLevel(Player sender, Avatar avatar, int skillId, int newLevel) {
if (avatar.setSkillLevel(skillId, newLevel)) {
long nameHash = GameData.getAvatarSkillDataMap().get(skillId).getNameTextMapHash();
var name = Language.getTextMapKey(nameHash);
CommandHandler.sendTranslatedMessage(
sender, "commands.talent.set_id", skillId, name, newLevel);
} else {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.out_of_range");
}
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
sendUsageMessage(sender);
return;
}
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
AvatarSkillDepotData skillDepot = avatar.getSkillDepot();
if (skillDepot
== null) { // Avatars without skill depots aren't a suitable target even with manual skillId
// specified
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id");
return;
}
int skillId = 0;
int newLevel = -1;
String cmdSwitch = args.get(0).toLowerCase();
switch (cmdSwitch) {
default -> {
sendUsageMessage(sender);
}
case "set" -> {
if (args.size() < 3) {
sendUsageMessage(sender);
return;
}
try {
skillId = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_skill_id");
return;
}
try {
newLevel = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
setTalentLevel(sender, avatar, skillId, newLevel);
}
case "n", "e", "q" -> {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
try {
newLevel = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
skillId =
switch (cmdSwitch) {
default -> skillDepot.getSkills().get(0);
case "e" -> skillDepot.getSkills().get(1);
case "q" -> skillDepot.getEnergySkill();
};
setTalentLevel(sender, avatar, skillId, newLevel);
}
case "all" -> {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
try {
newLevel = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.invalid_level");
return;
}
// This stops setTalentLevel from outputting 3 "levels out of range" messages
if (newLevel < 1 || newLevel > 15) {
CommandHandler.sendTranslatedMessage(sender, "commands.talent.out_of_range");
return;
}
int finalNewLevel = newLevel;
skillDepot
.getSkillsAndEnergySkill()
.forEach(id -> setTalentLevel(sender, avatar, id, finalNewLevel));
}
case "getid" -> {
var map = GameData.getAvatarSkillDataMap();
skillDepot
.getSkillsAndEnergySkill()
.forEach(
id -> {
var talent = map.get(id);
if (talent == null) return;
var talentName = Language.getTextMapKey(talent.getNameTextMapHash());
var talentDesc = Language.getTextMapKey(talent.getDescTextMapHash());
CommandHandler.sendTranslatedMessage(
sender, "commands.talent.id_desc", id, talentName, talentDesc);
});
}
}
}
}

View File

@@ -1,156 +1,156 @@
package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarPlayerData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
import emu.grasscutter.utils.JsonUtils;
import java.util.List;
@Command(
label = "trialAvatarActivity",
aliases = {"taa"},
usage = {
"change <scheduleId>",
"toggleDungeon <index(start from 1)|all>",
"toggleReward <index(start from 1)|all>"
},
permission = "player.trialavataractivity",
permissionTargeted = "player.trialavataractivity.others")
public final class TrialAvatarActivityCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
var action = args.get(0).toLowerCase();
var param = args.get(1);
var playerDataOption =
targetPlayer
.getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerDataOption.isEmpty()) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
var playerData = playerDataOption.get();
var handler = (TrialAvatarActivityHandler) playerData.getActivityHandler();
if (handler == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
var trialAvatarPlayerData =
JsonUtils.decode(playerData.getDetail(), TrialAvatarPlayerData.class);
if (trialAvatarPlayerData == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
switch (action) {
default -> this.sendUsageMessage(sender);
case "change" -> {
if (!param.chars().allMatch(Character::isDigit)) { // if its not number
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
if (TrialAvatarPlayerData.getAvatarIdList(Integer.parseInt(param)).isEmpty()) {
CommandHandler.sendMessage(
sender,
translate(
sender,
"commands.trialAvatarActivity.schedule_not_found",
Integer.parseInt(param)));
return;
}
playerData.setDetail(TrialAvatarPlayerData.create(Integer.parseInt(param)));
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_schedule", Integer.parseInt(param)));
}
case "toggledungeon" -> {
if (param.chars().allMatch(Character::isDigit)) { // if its number
if (Integer.parseInt(param) - 1 >= trialAvatarPlayerData.getRewardInfoList().size()
|| Integer.parseInt(param) - 1 <= 0) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
TrialAvatarPlayerData.RewardInfoItem rewardInfo =
trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param) - 1);
rewardInfo.setPassedDungeon(!rewardInfo.isPassedDungeon());
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_dungeon", Integer.parseInt(param)));
} else {
if (!param.equals("all")) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
trialAvatarPlayerData
.getRewardInfoList()
.forEach(r -> r.setPassedDungeon(!r.isPassedDungeon()));
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.success_dungeon_all"));
}
}
case "togglereward" -> {
if (param.chars().allMatch(Character::isDigit)) { // if its number
if (Integer.parseInt(param) - 1 >= trialAvatarPlayerData.getRewardInfoList().size()
|| Integer.parseInt(param) - 1 <= 0) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
TrialAvatarPlayerData.RewardInfoItem rewardInfo =
trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param) - 1);
rewardInfo.setReceivedReward(!rewardInfo.isReceivedReward());
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_reward", Integer.parseInt(param)));
} else {
if (!param.toLowerCase().equals("all")) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
trialAvatarPlayerData
.getRewardInfoList()
.forEach(r -> r.setReceivedReward(!r.isReceivedReward()));
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.success_reward_all"));
}
}
}
targetPlayer.sendPacket(
new PacketActivityInfoNotify(
handler.toProto(playerData, targetPlayer.getActivityManager().getConditionExecutor())));
}
}
package emu.grasscutter.command.commands;
import static emu.grasscutter.utils.Language.translate;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarPlayerData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
import emu.grasscutter.utils.JsonUtils;
import java.util.List;
@Command(
label = "trialAvatarActivity",
aliases = {"taa"},
usage = {
"change <scheduleId>",
"toggleDungeon <index(start from 1)|all>",
"toggleReward <index(start from 1)|all>"
},
permission = "player.trialavataractivity",
permissionTargeted = "player.trialavataractivity.others")
public final class TrialAvatarActivityCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
var action = args.get(0).toLowerCase();
var param = args.get(1);
var playerDataOption =
targetPlayer
.getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerDataOption.isEmpty()) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
var playerData = playerDataOption.get();
var handler = (TrialAvatarActivityHandler) playerData.getActivityHandler();
if (handler == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
var trialAvatarPlayerData =
JsonUtils.decode(playerData.getDetail(), TrialAvatarPlayerData.class);
if (trialAvatarPlayerData == null) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.not_found"));
return;
}
switch (action) {
default -> this.sendUsageMessage(sender);
case "change" -> {
if (!param.chars().allMatch(Character::isDigit)) { // if its not number
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
if (TrialAvatarPlayerData.getAvatarIdList(Integer.parseInt(param)).isEmpty()) {
CommandHandler.sendMessage(
sender,
translate(
sender,
"commands.trialAvatarActivity.schedule_not_found",
Integer.parseInt(param)));
return;
}
playerData.setDetail(TrialAvatarPlayerData.create(Integer.parseInt(param)));
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_schedule", Integer.parseInt(param)));
}
case "toggledungeon" -> {
if (param.chars().allMatch(Character::isDigit)) { // if its number
if (Integer.parseInt(param) - 1 >= trialAvatarPlayerData.getRewardInfoList().size()
|| Integer.parseInt(param) - 1 <= 0) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
TrialAvatarPlayerData.RewardInfoItem rewardInfo =
trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param) - 1);
rewardInfo.setPassedDungeon(!rewardInfo.isPassedDungeon());
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_dungeon", Integer.parseInt(param)));
} else {
if (!param.equals("all")) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
trialAvatarPlayerData
.getRewardInfoList()
.forEach(r -> r.setPassedDungeon(!r.isPassedDungeon()));
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.success_dungeon_all"));
}
}
case "togglereward" -> {
if (param.chars().allMatch(Character::isDigit)) { // if its number
if (Integer.parseInt(param) - 1 >= trialAvatarPlayerData.getRewardInfoList().size()
|| Integer.parseInt(param) - 1 <= 0) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
TrialAvatarPlayerData.RewardInfoItem rewardInfo =
trialAvatarPlayerData.getRewardInfoList().get(Integer.parseInt(param) - 1);
rewardInfo.setReceivedReward(!rewardInfo.isReceivedReward());
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender,
translate(
sender, "commands.trialAvatarActivity.success_reward", Integer.parseInt(param)));
} else {
if (!param.toLowerCase().equals("all")) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return;
}
trialAvatarPlayerData
.getRewardInfoList()
.forEach(r -> r.setReceivedReward(!r.isReceivedReward()));
playerData.setDetail(trialAvatarPlayerData);
playerData.save();
CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.success_reward_all"));
}
}
}
targetPlayer.sendPacket(
new PacketActivityInfoNotify(
handler.toProto(playerData, targetPlayer.getActivityManager().getConditionExecutor())));
}
}

View File

@@ -1,118 +1,118 @@
package emu.grasscutter.config;
import static emu.grasscutter.Grasscutter.config;
import emu.grasscutter.utils.FileUtils;
import java.nio.file.Path;
import java.util.Locale;
/**
* A data container for the server's configuration.
*
* <p>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;
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 DebugMode DEBUG_MODE_INFO = config.server.debugMode;
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;
private static final String DATA_FOLDER = config.folderStructure.data;
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;
/*
* Utilities
*/
@Deprecated(forRemoval = true)
public static String DATA() {
return DATA_FOLDER;
}
@Deprecated(forRemoval = true)
public static String DATA(String path) {
return Path.of(DATA_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static Path getResourcePath(String path) {
return FileUtils.getResourcePath(path);
}
@Deprecated(forRemoval = true)
public static String RESOURCE(String path) {
return FileUtils.getResourcePath(path).toString();
}
@Deprecated(forRemoval = true)
public static String PLUGIN() {
return PLUGINS_FOLDER;
}
public static String PLUGIN(String path) {
return Path.of(PLUGINS_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static String SCRIPT(String path) {
return Path.of(SCRIPTS_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static String PACKET(String path) {
return Path.of(PACKETS_FOLDER, path).toString();
}
/**
* Fallback method.
*
* @param left Attempt to use.
* @param right Use if left is undefined.
* @return Left or right.
*/
public static <T> T lr(T left, T right) {
return left == null ? right : left;
}
/**
* {@link Configuration#lr(Object, Object)} for {@link String}s.
*
* @param left Attempt to use.
* @param right Use if left is empty.
* @return Left or right.
*/
public static String lr(String left, String right) {
return left.isEmpty() ? right : left;
}
/**
* {@link Configuration#lr(Object, Object)} for {@link Integer}s.
*
* @param left Attempt to use.
* @param right Use if left is 0.
* @return Left or right.
*/
public static int lr(int left, int right) {
return left == 0 ? right : left;
}
}
package emu.grasscutter.config;
import static emu.grasscutter.Grasscutter.config;
import emu.grasscutter.utils.FileUtils;
import java.nio.file.Path;
import java.util.Locale;
/**
* A data container for the server's configuration.
*
* <p>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;
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 DebugMode DEBUG_MODE_INFO = config.server.debugMode;
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;
private static final String DATA_FOLDER = config.folderStructure.data;
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;
/*
* Utilities
*/
@Deprecated(forRemoval = true)
public static String DATA() {
return DATA_FOLDER;
}
@Deprecated(forRemoval = true)
public static String DATA(String path) {
return Path.of(DATA_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static Path getResourcePath(String path) {
return FileUtils.getResourcePath(path);
}
@Deprecated(forRemoval = true)
public static String RESOURCE(String path) {
return FileUtils.getResourcePath(path).toString();
}
@Deprecated(forRemoval = true)
public static String PLUGIN() {
return PLUGINS_FOLDER;
}
public static String PLUGIN(String path) {
return Path.of(PLUGINS_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static String SCRIPT(String path) {
return Path.of(SCRIPTS_FOLDER, path).toString();
}
@Deprecated(forRemoval = true)
public static String PACKET(String path) {
return Path.of(PACKETS_FOLDER, path).toString();
}
/**
* Fallback method.
*
* @param left Attempt to use.
* @param right Use if left is undefined.
* @return Left or right.
*/
public static <T> T lr(T left, T right) {
return left == null ? right : left;
}
/**
* {@link Configuration#lr(Object, Object)} for {@link String}s.
*
* @param left Attempt to use.
* @param right Use if left is empty.
* @return Left or right.
*/
public static String lr(String left, String right) {
return left.isEmpty() ? right : left;
}
/**
* {@link Configuration#lr(Object, Object)} for {@link Integer}s.
*
* @param left Attempt to use.
* @param right Use if left is 0.
* @return Left or right.
*/
public static int lr(int left, int right) {
return left == 0 ? right : left;
}
}

View File

@@ -1,86 +1,86 @@
package emu.grasscutter.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.ResourceLoader.AvatarConfig;
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.utils.WeightedList;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
public class GameDepot {
public static final int[] BLOCK_SIZE = new int[] {50, 500}; // Scales
private static final Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot =
new Int2ObjectOpenHashMap<>();
@Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
@Getter
private static final HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists =
new HashMap<>();
@Getter @Setter private static BlossomConfig blossomConfig;
public static void load() {
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.");
}
}
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
WeightedList<ReliquaryMainPropData> depotList = relicRandomMainPropDepot.get(depot);
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);
}
public static void addSpawnListById(
HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> data) {
spawnLists.putAll(data);
}
}
package emu.grasscutter.data;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.ResourceLoader.AvatarConfig;
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.utils.WeightedList;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
public class GameDepot {
public static final int[] BLOCK_SIZE = new int[] {50, 500}; // Scales
private static final Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot =
new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot =
new Int2ObjectOpenHashMap<>();
@Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
@Getter
private static final HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists =
new HashMap<>();
@Getter @Setter private static BlossomConfig blossomConfig;
public static void load() {
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.");
}
}
public static ReliquaryMainPropData getRandomRelicMainProp(int depot) {
WeightedList<ReliquaryMainPropData> depotList = relicRandomMainPropDepot.get(depot);
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);
}
public static void addSpawnListById(
HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> data) {
spawnLists.putAll(data);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,3 @@
package emu.grasscutter.data.binout.config;
public class ConfigEntityAvatar extends ConfigEntityBase {}
package emu.grasscutter.data.binout.config;
public class ConfigEntityAvatar extends ConfigEntityBase {}

View File

@@ -1,17 +1,17 @@
package emu.grasscutter.data.binout.config;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.binout.config.fields.ConfigCombat;
import emu.grasscutter.data.binout.config.fields.ConfigCommon;
import emu.grasscutter.data.binout.config.fields.ConfigGlobalValue;
import java.util.Collection;
import javax.annotation.Nullable;
import lombok.Data;
@Data
public class ConfigEntityBase {
@Nullable ConfigCommon configCommon;
@Nullable ConfigCombat combat;
Collection<ConfigAbilityData> abilities;
ConfigGlobalValue globalValue; // used for SGV in monsters and Gadgets
}
package emu.grasscutter.data.binout.config;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.binout.config.fields.ConfigCombat;
import emu.grasscutter.data.binout.config.fields.ConfigCommon;
import emu.grasscutter.data.binout.config.fields.ConfigGlobalValue;
import java.util.Collection;
import javax.annotation.Nullable;
import lombok.Data;
@Data
public class ConfigEntityBase {
@Nullable ConfigCommon configCommon;
@Nullable ConfigCombat combat;
Collection<ConfigAbilityData> abilities;
ConfigGlobalValue globalValue; // used for SGV in monsters and Gadgets
}

View File

@@ -1,11 +1,11 @@
package emu.grasscutter.data.binout.config;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigEntityGadget extends ConfigEntityBase {
// There are more values that can be added that might be useful in the json
}
package emu.grasscutter.data.binout.config;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigEntityGadget extends ConfigEntityBase {
// There are more values that can be added that might be useful in the json
}

View File

@@ -1,6 +1,6 @@
package emu.grasscutter.data.binout.config;
import lombok.Data;
@Data
public class ConfigEntityMonster extends ConfigEntityBase {}
package emu.grasscutter.data.binout.config;
import lombok.Data;
@Data
public class ConfigEntityMonster extends ConfigEntityBase {}

View File

@@ -1,13 +1,13 @@
package emu.grasscutter.data.binout.config;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import java.util.List;
import lombok.Getter;
public class ConfigLevelEntity {
@Getter private List<ConfigAbilityData> abilities; // monster abilities
@Getter private List<ConfigAbilityData> avatarAbilities;
@Getter private List<ConfigAbilityData> teamAbilities;
@Getter private List<Integer> preloadMonsterEntityIDs;
}
package emu.grasscutter.data.binout.config;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import java.util.List;
import lombok.Getter;
public class ConfigLevelEntity {
@Getter private List<ConfigAbilityData> abilities; // monster abilities
@Getter private List<ConfigAbilityData> avatarAbilities;
@Getter private List<ConfigAbilityData> teamAbilities;
@Getter private List<Integer> preloadMonsterEntityIDs;
}

View File

@@ -1,10 +1,10 @@
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigAbilityData {
public String abilityID;
public String abilityName;
public String abilityOverride;
}
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigAbilityData {
public String abilityID;
public String abilityName;
public String abilityOverride;
}

View File

@@ -1,8 +1,8 @@
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigAiBeta {
boolean enable;
}
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigAiBeta {
boolean enable;
}

View File

@@ -1,12 +1,12 @@
package emu.grasscutter.data.binout.config.fields;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombat {
// There are more values that can be added that might be useful in the json
ConfigCombatProperty property;
}
package emu.grasscutter.data.binout.config.fields;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombat {
// There are more values that can be added that might be useful in the json
ConfigCombatProperty property;
}

View File

@@ -1,18 +1,18 @@
package emu.grasscutter.data.binout.config.fields;
import com.google.gson.annotations.SerializedName;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombatDie {
@SerializedName(
value = "dieEndTime",
alternate = {"HGGPMFGGBNC"})
double dieEndTime;
double dieForceDisappearTime;
boolean hasAnimatorDie;
}
package emu.grasscutter.data.binout.config.fields;
import com.google.gson.annotations.SerializedName;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombatDie {
@SerializedName(
value = "dieEndTime",
alternate = {"HGGPMFGGBNC"})
double dieEndTime;
double dieForceDisappearTime;
boolean hasAnimatorDie;
}

View File

@@ -1,18 +1,18 @@
package emu.grasscutter.data.binout.config.fields;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombatProperty {
float HP;
boolean isLockHP;
boolean isInvincible;
boolean isGhostToAllied;
float attack;
float defence;
float weight;
boolean useCreatorProperty;
}
package emu.grasscutter.data.binout.config.fields;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ConfigCombatProperty {
float HP;
boolean isLockHP;
boolean isInvincible;
boolean isGhostToAllied;
float attack;
float defence;
float weight;
boolean useCreatorProperty;
}

View File

@@ -1,6 +1,6 @@
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigCommon {}
package emu.grasscutter.data.binout.config.fields;
import lombok.Data;
@Data
public class ConfigCommon {}

View File

@@ -1,12 +1,12 @@
package emu.grasscutter.data.binout.config.fields;
import java.util.Map;
import java.util.Set;
import lombok.Data;
/** Contains information about the entities SGVs */
@Data
public class ConfigGlobalValue {
Set<String> serverGlobalValues;
Map<String, Float> initServerGlobalValues;
}
package emu.grasscutter.data.binout.config.fields;
import java.util.Map;
import java.util.Set;
import lombok.Data;
/** Contains information about the entities SGVs */
@Data
public class ConfigGlobalValue {
Set<String> serverGlobalValues;
Map<String, Float> initServerGlobalValues;
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter;
public enum RotAngleType /*implements IntValueEnum */ {
ROT_NONE(-1),
ROT_ANGLE_X(0),
ROT_ANGLE_Y(1),
ROT_ANGLE_Z(2);
@Getter private final int id;
RotAngleType(int id) {
this.id = id;
}
// @Override
public int getValue() {
return id;
}
}
package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter;
public enum RotAngleType /*implements IntValueEnum */ {
ROT_NONE(-1),
ROT_ANGLE_X(0),
ROT_ANGLE_Y(1),
ROT_ANGLE_Z(2);
@Getter private final int id;
RotAngleType(int id) {
this.id = id;
}
// @Override
public int getValue() {
return id;
}
}

View File

@@ -1,7 +1,7 @@
package emu.grasscutter.data.binout.routes;
public enum RotType {
ROT_NONE,
ROT_ANGLE,
ROT_ROUND
}
package emu.grasscutter.data.binout.routes;
public enum RotType {
ROT_NONE,
ROT_ANGLE,
ROT_ROUND
}

View File

@@ -1,29 +1,29 @@
package emu.grasscutter.data.binout.routes;
import emu.grasscutter.net.proto.RouteOuterClass;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.val;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Route {
private int localId;
private String name;
private RouteType type = RouteType.Unknown;
private RoutePoint[] points;
private float arriveRange; // optional
private RotType rotType; // optional
private RotAngleType rotAngleType; // optional
public RouteOuterClass.Route toProto() {
val builder = RouteOuterClass.Route.newBuilder().setRouteType(type.getValue());
if (points != null) {
for (var routePoint : points) {
builder.addRoutePoints(routePoint.toProto().setArriveRange(arriveRange));
}
}
return builder.build();
}
}
package emu.grasscutter.data.binout.routes;
import emu.grasscutter.net.proto.RouteOuterClass;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.val;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class Route {
private int localId;
private String name;
private RouteType type = RouteType.Unknown;
private RoutePoint[] points;
private float arriveRange; // optional
private RotType rotType; // optional
private RotAngleType rotAngleType; // optional
public RouteOuterClass.Route toProto() {
val builder = RouteOuterClass.Route.newBuilder().setRouteType(type.getValue());
if (points != null) {
for (var routePoint : points) {
builder.addRoutePoints(routePoint.toProto().setArriveRange(arriveRange));
}
}
return builder.build();
}
}

View File

@@ -1,31 +1,31 @@
package emu.grasscutter.data.binout.routes;
import emu.grasscutter.net.proto.RoutePointOuterClass;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.val;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class RoutePoint {
private Position pos;
private int speedLevel; // optional
private float waitTime; // optional
private float targetVelocity; // optional
private boolean hasReachEvent; // optional
// rotRoundReachDir //optional Pos with optional values
// rotRoundLeaveDir //optional Pos with optional values
public RoutePointOuterClass.RoutePoint.Builder toProto() {
val builder = RoutePointOuterClass.RoutePoint.newBuilder().setPosition(pos.toProto());
if (waitTime != 0) {
builder.setTime(waitTime);
} else if (targetVelocity != 0) {
builder.setVelocity(targetVelocity);
}
return builder;
}
}
package emu.grasscutter.data.binout.routes;
import emu.grasscutter.net.proto.RoutePointOuterClass;
import emu.grasscutter.utils.Position;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
import lombok.val;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class RoutePoint {
private Position pos;
private int speedLevel; // optional
private float waitTime; // optional
private float targetVelocity; // optional
private boolean hasReachEvent; // optional
// rotRoundReachDir //optional Pos with optional values
// rotRoundLeaveDir //optional Pos with optional values
public RoutePointOuterClass.RoutePoint.Builder toProto() {
val builder = RoutePointOuterClass.RoutePoint.newBuilder().setPosition(pos.toProto());
if (waitTime != 0) {
builder.setTime(waitTime);
} else if (targetVelocity != 0) {
builder.setVelocity(targetVelocity);
}
return builder;
}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter;
public enum RouteType /*implements IntValueEnum*/ {
Unknown(-1),
OneWay(0),
Reciprocate(1),
Loop(2);
@Getter private final int id;
RouteType(int id) {
this.id = id;
}
// @Override
public int getValue() {
return id;
}
}
package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter;
public enum RouteType /*implements IntValueEnum*/ {
Unknown(-1),
OneWay(0),
Reciprocate(1),
Loop(2);
@Getter private final int id;
RouteType(int id) {
this.id = id;
}
// @Override
public int getValue() {
return id;
}
}

View File

@@ -1,13 +1,13 @@
package emu.grasscutter.data.binout.routes;
import javax.annotation.Nullable;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneRoutes {
private int sceneId;
@Nullable private Route[] routes;
}
package emu.grasscutter.data.binout.routes;
import javax.annotation.Nullable;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SceneRoutes {
private int sceneId;
@Nullable private Route[] routes;
}

View File

@@ -1,9 +1,9 @@
package emu.grasscutter.data.common;
import java.util.List;
public interface BaseTrialActivityData {
List<Integer> getAvatarIndexIdList();
List<Integer> getRewardIdList();
}
package emu.grasscutter.data.common;
import java.util.List;
public interface BaseTrialActivityData {
List<Integer> getAvatarIndexIdList();
List<Integer> getRewardIdList();
}

View File

@@ -1,70 +1,70 @@
package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.dungeon.DailyDungeonData;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
import lombok.Setter;
public final class PointData {
@Getter @Setter private int id;
private String $type;
@Getter private Position tranPos;
@Getter private Position pos;
@Getter private Position rot;
@Getter private Position size;
@SerializedName(
value = "dungeonIds",
alternate = {"JHHFPGJNMIN"})
@Getter
private int[] dungeonIds;
@SerializedName(
value = "dungeonRandomList",
alternate = {"OIBKFJNBLHO"})
@Getter
private int[] dungeonRandomList;
@SerializedName(
value = "groupIDs",
alternate = {"HFOBOOHKBGF"})
@Getter
private int[] groupIDs;
@SerializedName(
value = "tranSceneId",
alternate = {"JHBICGBAPIH"})
@Getter
@Setter
private int tranSceneId;
public String getType() {
return $type;
}
public void updateDailyDungeon() {
if (this.dungeonRandomList == null || this.dungeonRandomList.length == 0) {
return;
}
IntList newDungeons = new IntArrayList();
int day = Grasscutter.getCurrentDayOfWeek();
for (int randomId : this.dungeonRandomList) {
DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId);
if (data != null) {
for (int d : data.getDungeonsByDay(day)) {
newDungeons.add(d);
}
}
}
this.dungeonIds = newDungeons.toIntArray();
}
}
package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.dungeon.DailyDungeonData;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
import lombok.Setter;
public final class PointData {
@Getter @Setter private int id;
private String $type;
@Getter private Position tranPos;
@Getter private Position pos;
@Getter private Position rot;
@Getter private Position size;
@SerializedName(
value = "dungeonIds",
alternate = {"JHHFPGJNMIN"})
@Getter
private int[] dungeonIds;
@SerializedName(
value = "dungeonRandomList",
alternate = {"OIBKFJNBLHO"})
@Getter
private int[] dungeonRandomList;
@SerializedName(
value = "groupIDs",
alternate = {"HFOBOOHKBGF"})
@Getter
private int[] groupIDs;
@SerializedName(
value = "tranSceneId",
alternate = {"JHBICGBAPIH"})
@Getter
@Setter
private int tranSceneId;
public String getType() {
return $type;
}
public void updateDailyDungeon() {
if (this.dungeonRandomList == null || this.dungeonRandomList.length == 0) {
return;
}
IntList newDungeons = new IntArrayList();
int day = Grasscutter.getCurrentDayOfWeek();
for (int randomId : this.dungeonRandomList) {
DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId);
if (data != null) {
for (int d : data.getDungeonsByDay(day)) {
newDungeons.add(d);
}
}
}
this.dungeonIds = newDungeons.toIntArray();
}
}

View File

@@ -1,17 +1,17 @@
package emu.grasscutter.data.custom;
import emu.grasscutter.data.common.BaseTrialActivityData;
import java.util.List;
import lombok.*;
@Data
public class TrialAvatarActivityCustomData implements BaseTrialActivityData {
private int ScheduleId;
private List<Integer> AvatarIndexIdList;
private List<Integer> RewardIdList;
public void onLoad() {
this.AvatarIndexIdList = AvatarIndexIdList.stream().filter(x -> x > 0).toList();
this.RewardIdList = RewardIdList.stream().filter(x -> x > 0).toList();
}
}
package emu.grasscutter.data.custom;
import emu.grasscutter.data.common.BaseTrialActivityData;
import java.util.List;
import lombok.*;
@Data
public class TrialAvatarActivityCustomData implements BaseTrialActivityData {
private int ScheduleId;
private List<Integer> AvatarIndexIdList;
private List<Integer> RewardIdList;
public void onLoad() {
this.AvatarIndexIdList = AvatarIndexIdList.stream().filter(x -> x > 0).toList();
this.RewardIdList = RewardIdList.stream().filter(x -> x > 0).toList();
}
}

View File

@@ -1,16 +1,16 @@
package emu.grasscutter.data.custom;
import java.util.List;
import lombok.*;
@Data
public class TrialAvatarCustomData {
private int trialAvatarId;
private List<String> trialAvatarParamList;
private int coreProudSkillLevel;
private int skillDepotId;
public void onLoad() {
this.trialAvatarParamList = trialAvatarParamList.stream().filter(x -> !x.isBlank()).toList();
}
}
package emu.grasscutter.data.custom;
import java.util.List;
import lombok.*;
@Data
public class TrialAvatarCustomData {
private int trialAvatarId;
private List<String> trialAvatarParamList;
private int coreProudSkillLevel;
private int skillDepotId;
public void onLoad() {
this.trialAvatarParamList = trialAvatarParamList.stream().filter(x -> !x.isBlank()).toList();
}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.EntityType;
import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json")
@Getter
public final class GadgetData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private EntityType type;
private String jsonName;
private boolean isInteractive;
private String[] tags;
private String itemJsonName;
private long nameTextMapHash;
private int campId;
private String visionLevel;
}
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.EntityType;
import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json")
@Getter
public final class GadgetData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private EntityType type;
private String jsonName;
private boolean isInteractive;
private String[] tags;
private String itemJsonName;
private long nameTextMapHash;
private int campId;
private String visionLevel;
}

View File

@@ -1,27 +1,27 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@ResourceType(name = "GuideTriggerExcelConfigData.json")
public class GuideTriggerData extends GameResource {
// more like open state guide than quest guide
private int id; // dont use, just to prevent resource loader from not functioning
private String guideName;
private String type;
private String openState;
@Override
public int getId() {
return this.id;
}
public void onLoad() {
GameData.getGuideTriggerDataStringMap().put(getGuideName(), this);
}
}
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
@ResourceType(name = "GuideTriggerExcelConfigData.json")
public class GuideTriggerData extends GameResource {
// more like open state guide than quest guide
private int id; // dont use, just to prevent resource loader from not functioning
private String guideName;
private String type;
private String openState;
@Override
public int getId() {
return this.id;
}
public void onLoad() {
GameData.getGuideTriggerDataStringMap().put(getGuideName(), this);
}
}

View File

@@ -1,154 +1,154 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.game.inventory.EquipType;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.ItemUseTarget;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import lombok.Getter;
@ResourceType(
name = {
"MaterialExcelConfigData.json",
"WeaponExcelConfigData.json",
"ReliquaryExcelConfigData.json",
"HomeWorldFurnitureExcelConfigData.json"
})
@Getter
public class ItemData extends GameResource {
// Main
@Getter(onMethod_ = @Override)
private int id;
private final int stackLimit = 1;
private int maxUseCount;
private int rankLevel;
private String effectName;
private int rank;
private int weight;
private int gadgetId;
private int[] destroyReturnMaterial;
private int[] destroyReturnMaterialCount;
// Enums
private final ItemType itemType = ItemType.ITEM_NONE;
private MaterialType materialType = MaterialType.MATERIAL_NONE;
private EquipType equipType = EquipType.EQUIP_NONE;
private String effectType;
private String destroyRule;
// Food
private String foodQuality;
private int[] satiationParams;
// Usable item
private final ItemUseTarget useTarget = ItemUseTarget.ITEM_USE_TARGET_NONE;
private List<ItemUseData> itemUse;
private List<ItemUseAction> itemUseActions;
private final boolean useOnGain = false;
// Relic
private int mainPropDepotId;
private int appendPropDepotId;
private int appendPropNum;
private int setId;
private int[] addPropLevels;
private int baseConvExp;
private int maxLevel;
// Weapon
private int weaponPromoteId;
private int weaponBaseExp;
private int storyId;
private int avatarPromoteId;
private int awakenMaterial;
private int[] awakenCosts;
private int[] skillAffix;
private WeaponProperty[] weaponProp;
// Hash
private long nameTextMapHash;
// Furniture
private int comfort;
private List<Integer> furnType;
private List<Integer> furnitureGadgetID;
@SerializedName(
value = "roomSceneId",
alternate = {"BMEPAMCNABE", "DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK", "MFGACDIOHGF"})
private int roomSceneId;
// Custom
private transient IntSet addPropLevelSet;
public WeaponProperty[] getWeaponProperties() {
return this.weaponProp;
}
public boolean canAddRelicProp(int level) {
return this.addPropLevelSet != null && this.addPropLevelSet.contains(level);
}
public boolean isEquip() {
return this.itemType == ItemType.ITEM_RELIQUARY || this.itemType == ItemType.ITEM_WEAPON;
}
@Override
public void onLoad() {
if (this.itemType == ItemType.ITEM_RELIQUARY) {
if (this.addPropLevels != null && this.addPropLevels.length > 0) {
this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels);
}
} else if (this.itemType == ItemType.ITEM_WEAPON) {
this.equipType = EquipType.EQUIP_WEAPON;
} else {
this.equipType = EquipType.EQUIP_NONE;
}
if (this.weaponProp != null) {
this.weaponProp =
Arrays.stream(this.weaponProp)
.filter(prop -> prop.getPropType() != null)
.toArray(WeaponProperty[]::new);
}
if (this.getFurnType() != null) {
this.furnType = this.furnType.stream().filter(x -> x > 0).toList();
}
if (this.getFurnitureGadgetID() != null) {
this.furnitureGadgetID = this.furnitureGadgetID.stream().filter(x -> x > 0).toList();
}
// Prevent material type from being null
this.materialType = this.materialType == null ? MaterialType.MATERIAL_NONE : this.materialType;
if (this.itemUse != null && !this.itemUse.isEmpty()) {
this.itemUseActions =
this.itemUse.stream()
.filter(x -> x.getUseOp() != ItemUseOp.ITEM_USE_NONE)
.map(ItemUseAction::fromItemUseData)
.filter(Objects::nonNull)
.toList();
}
}
@Getter
public static class WeaponProperty {
private FightProperty propType;
private float initValue;
private String type;
}
}
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemUseData;
import emu.grasscutter.game.inventory.EquipType;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.inventory.MaterialType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.ItemUseAction.ItemUseAction;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.ItemUseTarget;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import lombok.Getter;
@ResourceType(
name = {
"MaterialExcelConfigData.json",
"WeaponExcelConfigData.json",
"ReliquaryExcelConfigData.json",
"HomeWorldFurnitureExcelConfigData.json"
})
@Getter
public class ItemData extends GameResource {
// Main
@Getter(onMethod_ = @Override)
private int id;
private final int stackLimit = 1;
private int maxUseCount;
private int rankLevel;
private String effectName;
private int rank;
private int weight;
private int gadgetId;
private int[] destroyReturnMaterial;
private int[] destroyReturnMaterialCount;
// Enums
private final ItemType itemType = ItemType.ITEM_NONE;
private MaterialType materialType = MaterialType.MATERIAL_NONE;
private EquipType equipType = EquipType.EQUIP_NONE;
private String effectType;
private String destroyRule;
// Food
private String foodQuality;
private int[] satiationParams;
// Usable item
private final ItemUseTarget useTarget = ItemUseTarget.ITEM_USE_TARGET_NONE;
private List<ItemUseData> itemUse;
private List<ItemUseAction> itemUseActions;
private final boolean useOnGain = false;
// Relic
private int mainPropDepotId;
private int appendPropDepotId;
private int appendPropNum;
private int setId;
private int[] addPropLevels;
private int baseConvExp;
private int maxLevel;
// Weapon
private int weaponPromoteId;
private int weaponBaseExp;
private int storyId;
private int avatarPromoteId;
private int awakenMaterial;
private int[] awakenCosts;
private int[] skillAffix;
private WeaponProperty[] weaponProp;
// Hash
private long nameTextMapHash;
// Furniture
private int comfort;
private List<Integer> furnType;
private List<Integer> furnitureGadgetID;
@SerializedName(
value = "roomSceneId",
alternate = {"BMEPAMCNABE", "DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK", "MFGACDIOHGF"})
private int roomSceneId;
// Custom
private transient IntSet addPropLevelSet;
public WeaponProperty[] getWeaponProperties() {
return this.weaponProp;
}
public boolean canAddRelicProp(int level) {
return this.addPropLevelSet != null && this.addPropLevelSet.contains(level);
}
public boolean isEquip() {
return this.itemType == ItemType.ITEM_RELIQUARY || this.itemType == ItemType.ITEM_WEAPON;
}
@Override
public void onLoad() {
if (this.itemType == ItemType.ITEM_RELIQUARY) {
if (this.addPropLevels != null && this.addPropLevels.length > 0) {
this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels);
}
} else if (this.itemType == ItemType.ITEM_WEAPON) {
this.equipType = EquipType.EQUIP_WEAPON;
} else {
this.equipType = EquipType.EQUIP_NONE;
}
if (this.weaponProp != null) {
this.weaponProp =
Arrays.stream(this.weaponProp)
.filter(prop -> prop.getPropType() != null)
.toArray(WeaponProperty[]::new);
}
if (this.getFurnType() != null) {
this.furnType = this.furnType.stream().filter(x -> x > 0).toList();
}
if (this.getFurnitureGadgetID() != null) {
this.furnitureGadgetID = this.furnitureGadgetID.stream().filter(x -> x > 0).toList();
}
// Prevent material type from being null
this.materialType = this.materialType == null ? MaterialType.MATERIAL_NONE : this.materialType;
if (this.itemUse != null && !this.itemUse.isEmpty()) {
this.itemUseActions =
this.itemUse.stream()
.filter(x -> x.getUseOp() != ItemUseOp.ITEM_USE_NONE)
.map(ItemUseAction::fromItemUseData)
.filter(Objects::nonNull)
.toList();
}
}
@Getter
public static class WeaponProperty {
private FightProperty propType;
private float initValue;
private String type;
}
}

View File

@@ -1,140 +1,140 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.quest.enums.*;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "QuestExcelConfigData.json")
@Getter
@ToString
public class QuestData extends GameResource {
@Getter private int subId;
@Getter private int mainId;
@Getter private int order;
@Getter private long descTextMapHash;
@Getter private boolean finishParent;
@Getter private boolean isRewind;
@Getter private LogicType acceptCondComb;
@Getter private LogicType finishCondComb;
@Getter private LogicType failCondComb;
@Getter private List<QuestAcceptCondition> acceptCond;
@Getter private List<QuestContentCondition> finishCond;
@Getter private List<QuestContentCondition> failCond;
@Getter private List<QuestExecParam> beginExec;
@Getter private List<QuestExecParam> finishExec;
@Getter private List<QuestExecParam> failExec;
@Getter private Guide guide;
@Getter private List<Integer> trialAvatarList;
public static String questConditionKey(
@Nonnull Enum<?> type, int firstParam, @Nullable String paramsStr) {
return type.name() + firstParam + (paramsStr != null ? paramsStr : "");
}
// ResourceLoader not happy if you remove getId() ~~
public int getId() {
return subId;
}
public void onLoad() {
this.acceptCond = acceptCond.stream().filter(p -> p.getType() != null).toList();
this.finishCond = finishCond.stream().filter(p -> p.getType() != null).toList();
this.failCond = failCond.stream().filter(p -> p.getType() != null).toList();
this.beginExec = beginExec.stream().filter(p -> p.type != null).toList();
this.finishExec = finishExec.stream().filter(p -> p.type != null).toList();
this.failExec = failExec.stream().filter(p -> p.type != null).toList();
if (this.acceptCondComb == null) this.acceptCondComb = LogicType.LOGIC_NONE;
if (this.finishCondComb == null) this.finishCondComb = LogicType.LOGIC_NONE;
if (this.failCondComb == null) this.failCondComb = LogicType.LOGIC_NONE;
addToCache();
}
private void addToCache() {
if (this.acceptCond == null) {
Grasscutter.getLogger().warn("missing AcceptConditions for quest {}", getSubId());
return;
}
var cacheMap = GameData.getBeginCondQuestMap();
if (getAcceptCond().isEmpty()) {
var list =
cacheMap.computeIfAbsent(
QuestData.questConditionKey(QuestCond.QUEST_COND_NONE, 0, null),
e -> new ArrayList<>());
list.add(this);
} else {
this.getAcceptCond()
.forEach(
questCondition -> {
if (questCondition.getType() == null) {
Grasscutter.getLogger().warn("null accept type for quest {}", getSubId());
return;
}
var key = questCondition.asKey();
var list = cacheMap.computeIfAbsent(key, e -> new ArrayList<>());
list.add(this);
});
}
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class QuestExecParam {
@SerializedName("_type")
QuestExec type;
@SerializedName("_param")
String[] param;
@SerializedName("_count")
String count;
}
public static class QuestAcceptCondition extends QuestCondition<QuestCond> {}
public static class QuestContentCondition extends QuestCondition<QuestContent> {}
@Data
public static class QuestCondition<TYPE extends Enum<?> & QuestTrigger> {
@SerializedName("_type")
private TYPE type;
@SerializedName("_param")
private int[] param;
@SerializedName("_param_str")
private String paramStr;
@SerializedName("_count")
private int count;
public String asKey() {
return questConditionKey(getType(), getParam()[0], getParamStr());
}
}
@Data
public static class Guide {
private String type;
private List<String> param;
private int guideScene;
}
}
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.quest.enums.*;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "QuestExcelConfigData.json")
@Getter
@ToString
public class QuestData extends GameResource {
@Getter private int subId;
@Getter private int mainId;
@Getter private int order;
@Getter private long descTextMapHash;
@Getter private boolean finishParent;
@Getter private boolean isRewind;
@Getter private LogicType acceptCondComb;
@Getter private LogicType finishCondComb;
@Getter private LogicType failCondComb;
@Getter private List<QuestAcceptCondition> acceptCond;
@Getter private List<QuestContentCondition> finishCond;
@Getter private List<QuestContentCondition> failCond;
@Getter private List<QuestExecParam> beginExec;
@Getter private List<QuestExecParam> finishExec;
@Getter private List<QuestExecParam> failExec;
@Getter private Guide guide;
@Getter private List<Integer> trialAvatarList;
public static String questConditionKey(
@Nonnull Enum<?> type, int firstParam, @Nullable String paramsStr) {
return type.name() + firstParam + (paramsStr != null ? paramsStr : "");
}
// ResourceLoader not happy if you remove getId() ~~
public int getId() {
return subId;
}
public void onLoad() {
this.acceptCond = acceptCond.stream().filter(p -> p.getType() != null).toList();
this.finishCond = finishCond.stream().filter(p -> p.getType() != null).toList();
this.failCond = failCond.stream().filter(p -> p.getType() != null).toList();
this.beginExec = beginExec.stream().filter(p -> p.type != null).toList();
this.finishExec = finishExec.stream().filter(p -> p.type != null).toList();
this.failExec = failExec.stream().filter(p -> p.type != null).toList();
if (this.acceptCondComb == null) this.acceptCondComb = LogicType.LOGIC_NONE;
if (this.finishCondComb == null) this.finishCondComb = LogicType.LOGIC_NONE;
if (this.failCondComb == null) this.failCondComb = LogicType.LOGIC_NONE;
addToCache();
}
private void addToCache() {
if (this.acceptCond == null) {
Grasscutter.getLogger().warn("missing AcceptConditions for quest {}", getSubId());
return;
}
var cacheMap = GameData.getBeginCondQuestMap();
if (getAcceptCond().isEmpty()) {
var list =
cacheMap.computeIfAbsent(
QuestData.questConditionKey(QuestCond.QUEST_COND_NONE, 0, null),
e -> new ArrayList<>());
list.add(this);
} else {
this.getAcceptCond()
.forEach(
questCondition -> {
if (questCondition.getType() == null) {
Grasscutter.getLogger().warn("null accept type for quest {}", getSubId());
return;
}
var key = questCondition.asKey();
var list = cacheMap.computeIfAbsent(key, e -> new ArrayList<>());
list.add(this);
});
}
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class QuestExecParam {
@SerializedName("_type")
QuestExec type;
@SerializedName("_param")
String[] param;
@SerializedName("_count")
String count;
}
public static class QuestAcceptCondition extends QuestCondition<QuestCond> {}
public static class QuestContentCondition extends QuestCondition<QuestContent> {}
@Data
public static class QuestCondition<TYPE extends Enum<?> & QuestTrigger> {
@SerializedName("_type")
private TYPE type;
@SerializedName("_param")
private int[] param;
@SerializedName("_param_str")
private String paramStr;
@SerializedName("_count")
private int count;
public String asKey() {
return questConditionKey(getType(), getParam()[0], getParamStr());
}
}
@Data
public static class Guide {
private String type;
private List<String> param;
private int guideScene;
}
}

View File

@@ -1,85 +1,85 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.RefreshType;
import emu.grasscutter.game.world.World;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "RefreshPolicyExcelConfigData.json")
public class RefreshPolicyExcelConfigData extends GameResource {
@Getter private int id;
@Getter private RefreshType type;
@Getter private String time;
private static int upperBound(List<Integer> list, int low, int high, int value) {
while (low < high) {
int middle = (high + low) / 2;
if (list.size() >= middle) return low; // Just in case
if (list.get(middle) > value) {
high = middle;
} else {
low = middle + 1;
}
}
return low;
}
public int getIntervalInSeconds(World world) {
if (time.isEmpty()) return -1;
var currentTimestamp = world.getTotalGameTimeMinutes();
try {
List<String> paramsStr = Arrays.asList(time.split(";"));
List<Integer> params = paramsStr.stream().map(Integer::parseInt).toList();
switch (type) {
case REFRESH_NONE:
return -1;
case REFRESH_INTERVAL:
if (params.isEmpty()) return -1;
return params.get(0);
case REFRESH_DAILY:
{
var dayTime = (world.getTotalGameTimeMinutes() / (24 * 60)) * 24 * 60 * 60;
var temp = currentTimestamp - dayTime;
var upper_bound_idx =
upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
}
return (params.get(upper_bound_idx - 1) - params.get(0));
}
case REFRESH_WEEKlY:
if (params.size() < 2) return -1;
{
var weekTime = (world.getTotalGameTimeDays() / 7) * 60 * 60 * 24 * 7;
var temp = currentTimestamp - weekTime;
var upper_bound_idx =
upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
}
return (params.get(upper_bound_idx - 1) - params.get(0));
}
case REFRESH_DAYBEGIN_INTERVAL:
if (params.size() == 0) return -1;
return params.get(0) * 60 * 60 * 24;
}
} catch (Exception e) {
}
return -1;
}
}
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.RefreshType;
import emu.grasscutter.game.world.World;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "RefreshPolicyExcelConfigData.json")
public class RefreshPolicyExcelConfigData extends GameResource {
@Getter private int id;
@Getter private RefreshType type;
@Getter private String time;
private static int upperBound(List<Integer> list, int low, int high, int value) {
while (low < high) {
int middle = (high + low) / 2;
if (list.size() >= middle) return low; // Just in case
if (list.get(middle) > value) {
high = middle;
} else {
low = middle + 1;
}
}
return low;
}
public int getIntervalInSeconds(World world) {
if (time.isEmpty()) return -1;
var currentTimestamp = world.getTotalGameTimeMinutes();
try {
List<String> paramsStr = Arrays.asList(time.split(";"));
List<Integer> params = paramsStr.stream().map(Integer::parseInt).toList();
switch (type) {
case REFRESH_NONE:
return -1;
case REFRESH_INTERVAL:
if (params.isEmpty()) return -1;
return params.get(0);
case REFRESH_DAILY:
{
var dayTime = (world.getTotalGameTimeMinutes() / (24 * 60)) * 24 * 60 * 60;
var temp = currentTimestamp - dayTime;
var upper_bound_idx =
upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
}
return (params.get(upper_bound_idx - 1) - params.get(0));
}
case REFRESH_WEEKlY:
if (params.size() < 2) return -1;
{
var weekTime = (world.getTotalGameTimeDays() / 7) * 60 * 60 * 24 * 7;
var temp = currentTimestamp - weekTime;
var upper_bound_idx =
upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
}
return (params.get(upper_bound_idx - 1) - params.get(0));
}
case REFRESH_DAYBEGIN_INTERVAL:
if (params.size() == 0) return -1;
return params.get(0) * 60 * 60 * 24;
}
} catch (Exception e) {
}
return -1;
}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "SceneExcelConfigData.json")
@Getter
public class SceneData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
@SerializedName("type")
private SceneType sceneType;
private String scriptData;
private String levelEntityConfig;
private List<Integer> specifiedAvatarList;
}
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "SceneExcelConfigData.json")
@Getter
public class SceneData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
@SerializedName("type")
private SceneType sceneType;
private String scriptData;
private String levelEntityConfig;
private List<Integer> specifiedAvatarList;
}

View File

@@ -1,100 +1,100 @@
package emu.grasscutter.data.excels.achievement;
import com.github.davidmoten.guavamini.Lists;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.BattlePassMissionData;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import lombok.Getter;
@Getter
@ResourceType(name = "AchievementExcelConfigData.json")
public class AchievementData extends GameResource {
private static final AtomicBoolean isDivided = new AtomicBoolean();
private int goalId;
private int preStageAchievementId;
private final Set<Integer> groupAchievementIdList = new HashSet<>();
private boolean isParent;
private long titleTextMapHash;
private long descTextMapHash;
private int finishRewardId;
private boolean isDeleteWatcherAfterFinish;
private int id;
private BattlePassMissionData.TriggerConfig triggerConfig;
private int progress;
private boolean isDisuse;
public static void divideIntoGroups() {
if (isDivided.get()) {
return;
}
isDivided.set(true);
var map = GameData.getAchievementDataMap();
var achievementDataList = map.values().stream().filter(AchievementData::isUsed).toList();
for (var data : achievementDataList) {
if (!data.hasPreStageAchievement() || data.hasGroupAchievements()) {
continue;
}
List<Integer> ids = Lists.newArrayList();
int parentId = data.getId();
while (true) {
var next = map.get(parentId + 1);
if (next == null || parentId != next.getPreStageAchievementId()) {
break;
}
parentId++;
}
map.get(parentId).isParent = true;
while (true) {
ids.add(parentId);
var previous = map.get(--parentId);
if (previous == null) {
break;
} else if (!previous.hasPreStageAchievement()) {
ids.add(parentId);
break;
}
}
for (int i : ids) {
map.get(i).groupAchievementIdList.addAll(ids);
}
}
map.values().stream()
.filter(a -> !a.hasGroupAchievements() && a.isUsed())
.forEach(a -> a.isParent = true);
}
public boolean hasPreStageAchievement() {
return this.preStageAchievementId != 0;
}
public boolean hasGroupAchievements() {
return !this.groupAchievementIdList.isEmpty();
}
public boolean isUsed() {
return !this.isDisuse;
}
public Set<Integer> getGroupAchievementIdList() {
return this.groupAchievementIdList.stream().collect(Collectors.toUnmodifiableSet());
}
public Set<Integer> getExcludedGroupAchievementIdList() {
return this.groupAchievementIdList.stream()
.filter(integer -> integer != this.getId())
.collect(Collectors.toUnmodifiableSet());
}
}
package emu.grasscutter.data.excels.achievement;
import com.github.davidmoten.guavamini.Lists;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.BattlePassMissionData;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import lombok.Getter;
@Getter
@ResourceType(name = "AchievementExcelConfigData.json")
public class AchievementData extends GameResource {
private static final AtomicBoolean isDivided = new AtomicBoolean();
private int goalId;
private int preStageAchievementId;
private final Set<Integer> groupAchievementIdList = new HashSet<>();
private boolean isParent;
private long titleTextMapHash;
private long descTextMapHash;
private int finishRewardId;
private boolean isDeleteWatcherAfterFinish;
private int id;
private BattlePassMissionData.TriggerConfig triggerConfig;
private int progress;
private boolean isDisuse;
public static void divideIntoGroups() {
if (isDivided.get()) {
return;
}
isDivided.set(true);
var map = GameData.getAchievementDataMap();
var achievementDataList = map.values().stream().filter(AchievementData::isUsed).toList();
for (var data : achievementDataList) {
if (!data.hasPreStageAchievement() || data.hasGroupAchievements()) {
continue;
}
List<Integer> ids = Lists.newArrayList();
int parentId = data.getId();
while (true) {
var next = map.get(parentId + 1);
if (next == null || parentId != next.getPreStageAchievementId()) {
break;
}
parentId++;
}
map.get(parentId).isParent = true;
while (true) {
ids.add(parentId);
var previous = map.get(--parentId);
if (previous == null) {
break;
} else if (!previous.hasPreStageAchievement()) {
ids.add(parentId);
break;
}
}
for (int i : ids) {
map.get(i).groupAchievementIdList.addAll(ids);
}
}
map.values().stream()
.filter(a -> !a.hasGroupAchievements() && a.isUsed())
.forEach(a -> a.isParent = true);
}
public boolean hasPreStageAchievement() {
return this.preStageAchievementId != 0;
}
public boolean hasGroupAchievements() {
return !this.groupAchievementIdList.isEmpty();
}
public boolean isUsed() {
return !this.isDisuse;
}
public Set<Integer> getGroupAchievementIdList() {
return this.groupAchievementIdList.stream().collect(Collectors.toUnmodifiableSet());
}
public Set<Integer> getExcludedGroupAchievementIdList() {
return this.groupAchievementIdList.stream()
.filter(integer -> integer != this.getId())
.collect(Collectors.toUnmodifiableSet());
}
}

View File

@@ -1,13 +1,13 @@
package emu.grasscutter.data.excels.achievement;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = "AchievementGoalExcelConfigData.json")
public class AchievementGoalData extends GameResource {
private int id;
private long nameTextMapHash;
private int finishRewardId;
}
package emu.grasscutter.data.excels.achievement;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = "AchievementGoalExcelConfigData.json")
public class AchievementGoalData extends GameResource {
private int id;
private long nameTextMapHash;
private int finishRewardId;
}

View File

@@ -1,38 +1,38 @@
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.activity.condition.ActivityConditions;
import emu.grasscutter.game.quest.enums.LogicType;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "NewActivityCondExcelConfigData.json")
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityCondExcelConfigData extends GameResource {
int condId;
LogicType condComb;
List<ActivityConfigCondition> cond;
public static class ActivityConfigCondition {
@Getter private ActivityConditions type;
@Getter private List<Integer> param;
public int[] paramArray() {
return param.stream().mapToInt(Integer::intValue).toArray();
}
}
@Override
public int getId() {
return condId;
}
@Override
public void onLoad() {
cond.removeIf(c -> c.type == null);
}
}
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.activity.condition.ActivityConditions;
import emu.grasscutter.game.quest.enums.LogicType;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "NewActivityCondExcelConfigData.json")
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityCondExcelConfigData extends GameResource {
int condId;
LogicType condComb;
List<ActivityConfigCondition> cond;
public static class ActivityConfigCondition {
@Getter private ActivityConditions type;
@Getter private List<Integer> param;
public int[] paramArray() {
return param.stream().mapToInt(Integer::intValue).toArray();
}
}
@Override
public int getId() {
return condId;
}
@Override
public void onLoad() {
cond.removeIf(c -> c.type == null);
}
}

View File

@@ -1,37 +1,37 @@
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(
name = "NewActivityExcelConfigData.json",
loadPriority = ResourceType.LoadPriority.LOW)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityData extends GameResource {
int activityId;
String activityType;
List<Integer> condGroupId;
List<Integer> watcherId;
List<ActivityWatcherData> watcherDataList;
@Override
public int getId() {
return this.activityId;
}
@Override
public void onLoad() {
this.watcherDataList =
watcherId.stream()
.map(item -> GameData.getActivityWatcherDataMap().get(item.intValue()))
.filter(Objects::nonNull)
.toList();
}
}
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(
name = "NewActivityExcelConfigData.json",
loadPriority = ResourceType.LoadPriority.LOW)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityData extends GameResource {
int activityId;
String activityType;
List<Integer> condGroupId;
List<Integer> watcherId;
List<ActivityWatcherData> watcherDataList;
@Override
public int getId() {
return this.activityId;
}
@Override
public void onLoad() {
this.watcherDataList =
watcherId.stream()
.map(item -> GameData.getActivityWatcherDataMap().get(item.intValue()))
.filter(Objects::nonNull)
.toList();
}
}

View File

@@ -1,24 +1,24 @@
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.shop.ShopType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "ActivityShopOverallExcelConfigData.json")
public class ActivityShopData extends GameResource {
@Getter private int scheduleId;
@Getter private ShopType shopType;
@Getter private List<Integer> sheetList;
@Override
public int getId() {
return getShopTypeId();
}
public int getShopTypeId() {
if (this.shopType == null) this.shopType = ShopType.SHOP_TYPE_NONE;
return shopType.shopTypeId;
}
}
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.shop.ShopType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "ActivityShopOverallExcelConfigData.json")
public class ActivityShopData extends GameResource {
@Getter private int scheduleId;
@Getter private ShopType shopType;
@Getter private List<Integer> sheetList;
@Override
public int getId() {
return getShopTypeId();
}
public int getShopTypeId() {
if (this.shopType == null) this.shopType = ShopType.SHOP_TYPE_NONE;
return shopType.shopTypeId;
}
}

View File

@@ -1,45 +1,45 @@
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.WatcherTriggerType;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(
name = "NewActivityWatcherConfigData.json",
loadPriority = ResourceType.LoadPriority.HIGH)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityWatcherData extends GameResource {
@Getter(onMethod_ = @Override)
int id;
int rewardID;
int progress;
WatcherTrigger triggerConfig;
@Override
public void onLoad() {
this.triggerConfig.paramList =
this.triggerConfig.paramList.stream().filter(x -> (x != null) && !x.isBlank()).toList();
this.triggerConfig.watcherTriggerType =
WatcherTriggerType.getTypeByName(this.triggerConfig.triggerType);
}
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class WatcherTrigger {
String triggerType;
List<String> paramList;
transient WatcherTriggerType watcherTriggerType;
public void onLoad() {
this.paramList = this.paramList.stream().filter(x -> (x != null) && !x.isBlank()).toList();
this.watcherTriggerType = WatcherTriggerType.getTypeByName(this.triggerType);
}
}
}
package emu.grasscutter.data.excels.activity;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.WatcherTriggerType;
import java.util.List;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(
name = "NewActivityWatcherConfigData.json",
loadPriority = ResourceType.LoadPriority.HIGH)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityWatcherData extends GameResource {
@Getter(onMethod_ = @Override)
int id;
int rewardID;
int progress;
WatcherTrigger triggerConfig;
@Override
public void onLoad() {
this.triggerConfig.paramList =
this.triggerConfig.paramList.stream().filter(x -> (x != null) && !x.isBlank()).toList();
this.triggerConfig.watcherTriggerType =
WatcherTriggerType.getTypeByName(this.triggerConfig.triggerType);
}
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public static class WatcherTrigger {
String triggerType;
List<String> paramList;
transient WatcherTriggerType watcherTriggerType;
public void onLoad() {
this.paramList = this.paramList.stream().filter(x -> (x != null) && !x.isBlank()).toList();
this.watcherTriggerType = WatcherTriggerType.getTypeByName(this.triggerType);
}
}
}

View File

@@ -1,38 +1,38 @@
package emu.grasscutter.data.excels.avatar;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarCostumeExcelConfigData.json")
public class AvatarCostumeData extends GameResource {
@SerializedName(value = "skinId", alternate = "costumeId")
private int skinId;
private int itemId;
private int characterId;
private int quality;
@Override
public int getId() {
return this.skinId;
}
public int getItemId() {
return this.itemId;
}
public int getCharacterId() {
return characterId;
}
public int getQuality() {
return quality;
}
@Override
public void onLoad() {
GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);
}
}
package emu.grasscutter.data.excels.avatar;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarCostumeExcelConfigData.json")
public class AvatarCostumeData extends GameResource {
@SerializedName(value = "skinId", alternate = "costumeId")
private int skinId;
private int itemId;
private int characterId;
private int quality;
@Override
public int getId() {
return this.skinId;
}
public int getItemId() {
return this.itemId;
}
public int getCharacterId() {
return characterId;
}
public int getQuality() {
return quality;
}
@Override
public void onLoad() {
GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);
}
}

View File

@@ -1,36 +1,36 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "AvatarCurveExcelConfigData.json")
public class AvatarCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public Map<String, Float> getCurveInfos() {
return curveInfoMap;
}
@Override
public void onLoad() {
this.curveInfoMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue()));
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "AvatarCurveExcelConfigData.json")
public class AvatarCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public Map<String, Float> getCurveInfos() {
return curveInfoMap;
}
@Override
public void onLoad() {
this.curveInfoMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue()));
}
}

View File

@@ -1,23 +1,23 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFettersLevelExcelConfigData.json")
public class AvatarFetterLevelData extends GameResource {
private int fetterLevel;
private int needExp;
@Override
public int getId() {
return this.fetterLevel;
}
public int getLevel() {
return fetterLevel;
}
public int getExp() {
return needExp;
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFettersLevelExcelConfigData.json")
public class AvatarFetterLevelData extends GameResource {
private int fetterLevel;
private int needExp;
@Override
public int getId() {
return this.fetterLevel;
}
public int getLevel() {
return fetterLevel;
}
public int getExp() {
return needExp;
}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFlycloakExcelConfigData.json")
public class AvatarFlycloakData extends GameResource {
private int flycloakId;
private long nameTextMapHash;
@Override
public int getId() {
return this.flycloakId;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
@Override
public void onLoad() {}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFlycloakExcelConfigData.json")
public class AvatarFlycloakData extends GameResource {
private int flycloakId;
private long nameTextMapHash;
@Override
public int getId() {
return this.flycloakId;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
@Override
public void onLoad() {}
}

View File

@@ -1,23 +1,23 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarLevelExcelConfigData.json")
public class AvatarLevelData extends GameResource {
private int level;
private int exp;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarLevelExcelConfigData.json")
public class AvatarLevelData extends GameResource {
private int level;
private int exp;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
}

View File

@@ -1,74 +1,74 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData;
import java.util.ArrayList;
@ResourceType(name = "AvatarPromoteExcelConfigData.json")
public class AvatarPromoteData extends GameResource {
private int avatarPromoteId;
private int promoteLevel;
private int scoinCost;
private ItemParamData[] costItems;
private int unlockMaxLevel;
private FightPropData[] addProps;
private int requiredPlayerLevel;
@Override
public int getId() {
return (avatarPromoteId << 8) + promoteLevel;
}
public int getAvatarPromoteId() {
return avatarPromoteId;
}
public int getPromoteLevel() {
return promoteLevel;
}
public ItemParamData[] getCostItems() {
return costItems;
}
public int getCoinCost() {
return scoinCost;
}
public FightPropData[] getAddProps() {
return addProps;
}
public int getUnlockMaxLevel() {
return unlockMaxLevel;
}
public int getRequiredPlayerLevel() {
return requiredPlayerLevel;
}
@Override
public void onLoad() {
// Trim item params
ArrayList<ItemParamData> trim = new ArrayList<>(getAddProps().length);
for (ItemParamData itemParam : getCostItems()) {
if (itemParam.getId() == 0) {
continue;
}
trim.add(itemParam);
}
this.costItems = trim.toArray(new ItemParamData[trim.size()]);
// Trim fight prop data (just in case)
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData;
import java.util.ArrayList;
@ResourceType(name = "AvatarPromoteExcelConfigData.json")
public class AvatarPromoteData extends GameResource {
private int avatarPromoteId;
private int promoteLevel;
private int scoinCost;
private ItemParamData[] costItems;
private int unlockMaxLevel;
private FightPropData[] addProps;
private int requiredPlayerLevel;
@Override
public int getId() {
return (avatarPromoteId << 8) + promoteLevel;
}
public int getAvatarPromoteId() {
return avatarPromoteId;
}
public int getPromoteLevel() {
return promoteLevel;
}
public ItemParamData[] getCostItems() {
return costItems;
}
public int getCoinCost() {
return scoinCost;
}
public FightPropData[] getAddProps() {
return addProps;
}
public int getUnlockMaxLevel() {
return unlockMaxLevel;
}
public int getRequiredPlayerLevel() {
return requiredPlayerLevel;
}
@Override
public void onLoad() {
// Trim item params
ArrayList<ItemParamData> trim = new ArrayList<>(getAddProps().length);
for (ItemParamData itemParam : getCostItems()) {
if (itemParam.getId() == 0) {
continue;
}
trim.add(itemParam);
}
this.costItems = trim.toArray(new ItemParamData[trim.size()]);
// Trim fight prop data (just in case)
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}

View File

@@ -1,24 +1,24 @@
package emu.grasscutter.data.excels.avatar;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@ResourceType(name = "AvatarReplaceCostumeExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
public class AvatarReplaceCostumeData extends GameResource {
private int avatarId;
@SerializedName(
value = "costumeId",
alternate = {"MGLCOPOIJIC", "BDBMOBGKIAP"})
private int costumeId;
@Override
public int getId() {
return costumeId;
}
}
package emu.grasscutter.data.excels.avatar;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@ResourceType(name = "AvatarReplaceCostumeExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
public class AvatarReplaceCostumeData extends GameResource {
private int avatarId;
@SerializedName(
value = "costumeId",
alternate = {"MGLCOPOIJIC", "BDBMOBGKIAP"})
private int costumeId;
@Override
public int getId() {
return costumeId;
}
}

View File

@@ -1,25 +1,25 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.game.props.ElementType;
import lombok.Getter;
@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
@Getter
public class AvatarSkillData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private float cdTime;
private int costElemVal;
private int maxChargeNum;
private int triggerID;
private boolean isAttackCameraLock;
private int proudSkillGroupId;
private ElementType costElemType;
private long nameTextMapHash;
private long descTextMapHash;
private String abilityName;
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.game.props.ElementType;
import lombok.Getter;
@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
@Getter
public class AvatarSkillData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private float cdTime;
private int costElemVal;
private int maxChargeNum;
private int triggerID;
private boolean isAttackCameraLock;
private int proudSkillGroupId;
private ElementType costElemType;
private long nameTextMapHash;
private long descTextMapHash;
private String abilityName;
}

View File

@@ -1,89 +1,89 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceLoader.AvatarConfig;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import lombok.Getter;
@ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class AvatarSkillDepotData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int energySkill;
private int attackModeSkill;
private List<Integer> skills;
private List<Integer> subSkills;
private List<String> extraAbilities;
private List<Integer> talents;
private List<InherentProudSkillOpens> inherentProudSkillOpens;
private String talentStarName;
private String skillDepotAbilityGroup;
// Transient
private AvatarSkillData energySkillData;
private ElementType elementType;
private IntList abilities;
private int talentCostItemId;
public void setAbilities(AbilityEmbryoEntry info) {
this.abilities = new IntArrayList(info.getAbilities().length);
for (String ability : info.getAbilities()) {
this.abilities.add(Utils.abilityHash(ability));
}
}
@Override
public void onLoad() {
// Set energy skill data
this.energySkillData = GameData.getAvatarSkillDataMap().get(this.energySkill);
if (this.energySkillData != null) {
this.elementType = this.energySkillData.getCostElemType();
} else {
this.elementType = ElementType.None;
}
// Set embryo abilities (if player skill depot)
if (getSkillDepotAbilityGroup() != null && getSkillDepotAbilityGroup().length() > 0) {
AvatarConfig config = GameDepot.getPlayerAbilities().get(getSkillDepotAbilityGroup());
if (config != null) {
this.setAbilities(
new AbilityEmbryoEntry(
getSkillDepotAbilityGroup(),
config.abilities.stream().map(Object::toString).toArray(String[]::new)));
}
}
// Get constellation item from GameData
Optional.ofNullable(this.talents)
.map(talents -> talents.get(0))
.map(i -> GameData.getAvatarTalentDataMap().get((int) i))
.map(talentData -> talentData.getMainCostItemId())
.ifPresent(itemId -> this.talentCostItemId = itemId);
}
public IntStream getSkillsAndEnergySkill() {
return IntStream.concat(this.skills.stream().mapToInt(i -> i), IntStream.of(this.energySkill))
.filter(skillId -> skillId > 0);
}
@Getter
public static class InherentProudSkillOpens {
private int proudSkillGroupId;
private int needAvatarPromoteLevel;
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceLoader.AvatarConfig;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
import lombok.Getter;
@ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class AvatarSkillDepotData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int energySkill;
private int attackModeSkill;
private List<Integer> skills;
private List<Integer> subSkills;
private List<String> extraAbilities;
private List<Integer> talents;
private List<InherentProudSkillOpens> inherentProudSkillOpens;
private String talentStarName;
private String skillDepotAbilityGroup;
// Transient
private AvatarSkillData energySkillData;
private ElementType elementType;
private IntList abilities;
private int talentCostItemId;
public void setAbilities(AbilityEmbryoEntry info) {
this.abilities = new IntArrayList(info.getAbilities().length);
for (String ability : info.getAbilities()) {
this.abilities.add(Utils.abilityHash(ability));
}
}
@Override
public void onLoad() {
// Set energy skill data
this.energySkillData = GameData.getAvatarSkillDataMap().get(this.energySkill);
if (this.energySkillData != null) {
this.elementType = this.energySkillData.getCostElemType();
} else {
this.elementType = ElementType.None;
}
// Set embryo abilities (if player skill depot)
if (getSkillDepotAbilityGroup() != null && getSkillDepotAbilityGroup().length() > 0) {
AvatarConfig config = GameDepot.getPlayerAbilities().get(getSkillDepotAbilityGroup());
if (config != null) {
this.setAbilities(
new AbilityEmbryoEntry(
getSkillDepotAbilityGroup(),
config.abilities.stream().map(Object::toString).toArray(String[]::new)));
}
}
// Get constellation item from GameData
Optional.ofNullable(this.talents)
.map(talents -> talents.get(0))
.map(i -> GameData.getAvatarTalentDataMap().get((int) i))
.map(talentData -> talentData.getMainCostItemId())
.ifPresent(itemId -> this.talentCostItemId = itemId);
}
public IntStream getSkillsAndEnergySkill() {
return IntStream.concat(this.skills.stream().mapToInt(i -> i), IntStream.of(this.energySkill))
.filter(skillId -> skillId > 0);
}
@Getter
public static class InherentProudSkillOpens {
private int proudSkillGroupId;
private int needAvatarPromoteLevel;
}
}

View File

@@ -1,69 +1,69 @@
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.FightPropData;
import java.util.ArrayList;
@ResourceType(name = "AvatarTalentExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
public class AvatarTalentData extends GameResource {
private int talentId;
private int prevTalent;
private long nameTextMapHash;
private String icon;
private int mainCostItemId;
private int mainCostItemCount;
private String openConfig;
private FightPropData[] addProps;
private float[] paramList;
@Override
public int getId() {
return this.talentId;
}
public int PrevTalent() {
return prevTalent;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public String getIcon() {
return icon;
}
public int getMainCostItemId() {
return mainCostItemId;
}
public int getMainCostItemCount() {
return mainCostItemCount;
}
public String getOpenConfig() {
return openConfig;
}
public FightPropData[] getAddProps() {
return addProps;
}
public float[] getParamList() {
return paramList;
}
@Override
public void onLoad() {
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null || prop.getValue() == 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}
package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.FightPropData;
import java.util.ArrayList;
@ResourceType(name = "AvatarTalentExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
public class AvatarTalentData extends GameResource {
private int talentId;
private int prevTalent;
private long nameTextMapHash;
private String icon;
private int mainCostItemId;
private int mainCostItemCount;
private String openConfig;
private FightPropData[] addProps;
private float[] paramList;
@Override
public int getId() {
return this.talentId;
}
public int PrevTalent() {
return prevTalent;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public String getIcon() {
return icon;
}
public int getMainCostItemId() {
return mainCostItemId;
}
public int getMainCostItemCount() {
return mainCostItemCount;
}
public String getOpenConfig() {
return openConfig;
}
public FightPropData[] getAddProps() {
return addProps;
}
public float[] getParamList() {
return paramList;
}
@Override
public void onLoad() {
ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null || prop.getValue() == 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}

View File

@@ -1,27 +1,27 @@
package emu.grasscutter.data.excels.codex;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = {"AnimalCodexExcelConfigData.json"})
@Getter
public class CodexAnimalData extends GameResource {
@Getter(onMethod_ = @Override)
private int Id;
private String type;
private int describeId;
private int sortOrder;
@SerializedName(
value = "countType",
alternate = {"OCCLHPBCDGL"})
private CountType countType;
public enum CountType {
CODEX_COUNT_TYPE_KILL,
CODEX_COUNT_TYPE_CAPTURE
}
}
package emu.grasscutter.data.excels.codex;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = {"AnimalCodexExcelConfigData.json"})
@Getter
public class CodexAnimalData extends GameResource {
@Getter(onMethod_ = @Override)
private int Id;
private String type;
private int describeId;
private int sortOrder;
@SerializedName(
value = "countType",
alternate = {"OCCLHPBCDGL"})
private CountType countType;
public enum CountType {
CODEX_COUNT_TYPE_KILL,
CODEX_COUNT_TYPE_CAPTURE
}
}

View File

@@ -1,29 +1,29 @@
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"MaterialCodexExcelConfigData.json"})
public class CodexMaterialData extends GameResource {
private int Id;
private int materialId;
private int sortOrder;
public int getSortOrder() {
return sortOrder;
}
public int getMaterialId() {
return materialId;
}
public int getId() {
return Id;
}
@Override
public void onLoad() {
GameData.getCodexMaterialDataIdMap().put(this.getMaterialId(), this);
}
}
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"MaterialCodexExcelConfigData.json"})
public class CodexMaterialData extends GameResource {
private int Id;
private int materialId;
private int sortOrder;
public int getSortOrder() {
return sortOrder;
}
public int getMaterialId() {
return materialId;
}
public int getId() {
return Id;
}
@Override
public void onLoad() {
GameData.getCodexMaterialDataIdMap().put(this.getMaterialId(), this);
}
}

View File

@@ -1,41 +1,41 @@
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"QuestCodexExcelConfigData.json"})
public class CodexQuestData extends GameResource {
private int Id;
private int parentQuestId;
private int chapterId;
private int sortOrder;
private boolean isDisuse;
public int getParentQuestId() {
return parentQuestId;
}
public int getId() {
return Id;
}
public int getChapterId() {
return chapterId;
}
public int getSortOrder() {
return sortOrder;
}
public boolean getIsDisuse() {
return isDisuse;
}
@Override
public void onLoad() {
if (!this.getIsDisuse()) {
GameData.getCodexQuestDataIdMap().put(this.getParentQuestId(), this);
}
}
}
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"QuestCodexExcelConfigData.json"})
public class CodexQuestData extends GameResource {
private int Id;
private int parentQuestId;
private int chapterId;
private int sortOrder;
private boolean isDisuse;
public int getParentQuestId() {
return parentQuestId;
}
public int getId() {
return Id;
}
public int getChapterId() {
return chapterId;
}
public int getSortOrder() {
return sortOrder;
}
public boolean getIsDisuse() {
return isDisuse;
}
@Override
public void onLoad() {
if (!this.getIsDisuse()) {
GameData.getCodexQuestDataIdMap().put(this.getParentQuestId(), this);
}
}
}

View File

@@ -1,47 +1,47 @@
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
@ResourceType(name = {"ReliquaryCodexExcelConfigData.json"})
public class CodexReliquaryData extends GameResource {
@Getter private int Id;
@Getter private int suitId;
@Getter private int level;
@Getter private int cupId;
@Getter private int leatherId;
@Getter private int capId;
@Getter private int flowerId;
@Getter private int sandId;
@Getter private int sortOrder;
private transient IntCollection ids;
public boolean containsId(int id) {
return getIds().contains(id);
}
public IntCollection getIds() {
if (this.ids == null) {
int[] idsArr = {cupId, leatherId, capId, flowerId, sandId};
this.ids = IntList.of(idsArr);
}
return this.ids;
}
@Override
public void onLoad() {
// Normalize all itemIds to the 0-substat form
cupId = (cupId / 10) * 10;
leatherId = (leatherId / 10) * 10;
capId = (capId / 10) * 10;
flowerId = (flowerId / 10) * 10;
sandId = (sandId / 10) * 10;
GameData.getCodexReliquaryArrayList().add(this);
GameData.getCodexReliquaryDataIdMap().put(getSuitId(), this);
}
}
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
@ResourceType(name = {"ReliquaryCodexExcelConfigData.json"})
public class CodexReliquaryData extends GameResource {
@Getter private int Id;
@Getter private int suitId;
@Getter private int level;
@Getter private int cupId;
@Getter private int leatherId;
@Getter private int capId;
@Getter private int flowerId;
@Getter private int sandId;
@Getter private int sortOrder;
private transient IntCollection ids;
public boolean containsId(int id) {
return getIds().contains(id);
}
public IntCollection getIds() {
if (this.ids == null) {
int[] idsArr = {cupId, leatherId, capId, flowerId, sandId};
this.ids = IntList.of(idsArr);
}
return this.ids;
}
@Override
public void onLoad() {
// Normalize all itemIds to the 0-substat form
cupId = (cupId / 10) * 10;
leatherId = (leatherId / 10) * 10;
capId = (capId / 10) * 10;
flowerId = (flowerId / 10) * 10;
sandId = (sandId / 10) * 10;
GameData.getCodexReliquaryArrayList().add(this);
GameData.getCodexReliquaryDataIdMap().put(getSuitId(), this);
}
}

View File

@@ -1,29 +1,29 @@
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"WeaponCodexExcelConfigData.json"})
public class CodexWeaponData extends GameResource {
private int Id;
private int weaponId;
private int sortOrder;
public int getSortOrder() {
return sortOrder;
}
public int getWeaponId() {
return weaponId;
}
public int getId() {
return Id;
}
@Override
public void onLoad() {
GameData.getCodexWeaponDataIdMap().put(this.getWeaponId(), this);
}
}
package emu.grasscutter.data.excels.codex;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = {"WeaponCodexExcelConfigData.json"})
public class CodexWeaponData extends GameResource {
private int Id;
private int weaponId;
private int sortOrder;
public int getSortOrder() {
return sortOrder;
}
public int getWeaponId() {
return weaponId;
}
public int getId() {
return Id;
}
@Override
public void onLoad() {
GameData.getCodexWeaponDataIdMap().put(this.getWeaponId(), this);
}
}

View File

@@ -1,44 +1,44 @@
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Calendar;
import lombok.Getter;
@ResourceType(name = "DailyDungeonConfigData.json")
public class DailyDungeonData extends GameResource {
private static final int[] empty = new int[0];
private final Int2ObjectMap<int[]> map;
@Getter(onMethod_ = @Override)
private int id;
private int[] monday;
private int[] tuesday;
private int[] wednesday;
private int[] thursday;
private int[] friday;
private int[] saturday;
private int[] sunday;
public DailyDungeonData() {
this.map = new Int2ObjectOpenHashMap<>();
}
public int[] getDungeonsByDay(int day) {
return map.getOrDefault(day, empty);
}
@Override
public void onLoad() {
map.put(Calendar.MONDAY, monday);
map.put(Calendar.TUESDAY, tuesday);
map.put(Calendar.WEDNESDAY, wednesday);
map.put(Calendar.THURSDAY, thursday);
map.put(Calendar.FRIDAY, friday);
map.put(Calendar.SATURDAY, saturday);
map.put(Calendar.SUNDAY, sunday);
}
}
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.util.Calendar;
import lombok.Getter;
@ResourceType(name = "DailyDungeonConfigData.json")
public class DailyDungeonData extends GameResource {
private static final int[] empty = new int[0];
private final Int2ObjectMap<int[]> map;
@Getter(onMethod_ = @Override)
private int id;
private int[] monday;
private int[] tuesday;
private int[] wednesday;
private int[] thursday;
private int[] friday;
private int[] saturday;
private int[] sunday;
public DailyDungeonData() {
this.map = new Int2ObjectOpenHashMap<>();
}
public int[] getDungeonsByDay(int day) {
return map.getOrDefault(day, empty);
}
@Override
public void onLoad() {
map.put(Calendar.MONDAY, monday);
map.put(Calendar.TUESDAY, tuesday);
map.put(Calendar.WEDNESDAY, wednesday);
map.put(Calendar.THURSDAY, thursday);
map.put(Calendar.FRIDAY, friday);
map.put(Calendar.SATURDAY, saturday);
map.put(Calendar.SUNDAY, sunday);
}
}

View File

@@ -1,82 +1,82 @@
package emu.grasscutter.data.excels.dungeon;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import java.util.HashSet;
import lombok.Getter;
@Getter
@ResourceType(name = "DungeonChallengeConfigData.json")
public class DungeonChallengeConfigData extends GameResource {
private int id;
private ChallengeType challengeType;
private boolean noSuccessHint;
private boolean noFailHint;
private boolean isBlockTopTimer;
private int subChallengeFadeOutDelayTime;
private int activitySkillId;
private HashSet<String> teamAbilityGroupList;
private SubChallengeFadeOutType subChallengeFadeOutRule;
private SubChallengeBannerType subChallengeBannerRule;
private InterruptButtonType interruptButtonType;
@SerializedName(
value = "subChallengeSortType",
alternate = {"PNCLDNBHKDJ"})
private SubChallengeSortType subChallengeSortType;
@SerializedName(
value = "animationOnSubStart",
alternate = {"DNFAFNMMMDP"})
private AllowAnimationType animationOnSubStart;
@SerializedName(
value = "animationOnSubSuccess",
alternate = {"ENONHOGJDDN"})
private AllowAnimationType animationOnSubSuccess;
@SerializedName(
value = "animationOnSubFail",
alternate = {"NJBJIKAIENN"})
private AllowAnimationType animationOnSubFail;
public int getId() {
return id;
}
public enum InterruptButtonType {
INTERRUPT_BUTTON_TYPE_NONE,
INTERRUPT_BUTTON_TYPE_HOST,
INTERRUPT_BUTTON_TYPE_ALL
}
public enum SubChallengeFadeOutType {
SUBCHALLENGE_FADEOUT_TYPE_NONE,
SUBCHALLENGE_FADEOUT_TYPE_SUCCESS,
SUBCHALLENGE_FADEOUT_TYPE_FAIL,
SUBCHALLENGE_FADEOUT_TYPE_FINISH
}
public enum SubChallengeBannerType {
SUBCHALLENGE_BANNER_TYPE_NONE,
SUBCHALLENGE_BANNER_TYPE_SUCCESS,
SUBCHALLENGE_BANNER_TYPE_FAIL,
SUBCHALLENGE_BANNER_TYPE_HIDE_FINAL,
SUBCHALLENGE_BANNER_TYPE_SHOW_FINAL
}
public enum SubChallengeSortType {
SUB_CHALLENGE_SORT_TYPE_DEFAULT,
SUB_CHALLENGE_SORT_TYPE_CHALLENGEINDEX
}
public enum AllowAnimationType {
SUB_CHALLENGE_ANIM_TYPE_DEFAULT,
SUB_CHALLENGE_ANIM_TYPE_FORBID,
SUB_CHALLENGE_ANIM_TYPE_SUCCESS,
SUB_CHALLENGE_ANIM_TYPE_FAIL
}
}
package emu.grasscutter.data.excels.dungeon;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
import java.util.HashSet;
import lombok.Getter;
@Getter
@ResourceType(name = "DungeonChallengeConfigData.json")
public class DungeonChallengeConfigData extends GameResource {
private int id;
private ChallengeType challengeType;
private boolean noSuccessHint;
private boolean noFailHint;
private boolean isBlockTopTimer;
private int subChallengeFadeOutDelayTime;
private int activitySkillId;
private HashSet<String> teamAbilityGroupList;
private SubChallengeFadeOutType subChallengeFadeOutRule;
private SubChallengeBannerType subChallengeBannerRule;
private InterruptButtonType interruptButtonType;
@SerializedName(
value = "subChallengeSortType",
alternate = {"PNCLDNBHKDJ"})
private SubChallengeSortType subChallengeSortType;
@SerializedName(
value = "animationOnSubStart",
alternate = {"DNFAFNMMMDP"})
private AllowAnimationType animationOnSubStart;
@SerializedName(
value = "animationOnSubSuccess",
alternate = {"ENONHOGJDDN"})
private AllowAnimationType animationOnSubSuccess;
@SerializedName(
value = "animationOnSubFail",
alternate = {"NJBJIKAIENN"})
private AllowAnimationType animationOnSubFail;
public int getId() {
return id;
}
public enum InterruptButtonType {
INTERRUPT_BUTTON_TYPE_NONE,
INTERRUPT_BUTTON_TYPE_HOST,
INTERRUPT_BUTTON_TYPE_ALL
}
public enum SubChallengeFadeOutType {
SUBCHALLENGE_FADEOUT_TYPE_NONE,
SUBCHALLENGE_FADEOUT_TYPE_SUCCESS,
SUBCHALLENGE_FADEOUT_TYPE_FAIL,
SUBCHALLENGE_FADEOUT_TYPE_FINISH
}
public enum SubChallengeBannerType {
SUBCHALLENGE_BANNER_TYPE_NONE,
SUBCHALLENGE_BANNER_TYPE_SUCCESS,
SUBCHALLENGE_BANNER_TYPE_FAIL,
SUBCHALLENGE_BANNER_TYPE_HIDE_FINAL,
SUBCHALLENGE_BANNER_TYPE_SHOW_FINAL
}
public enum SubChallengeSortType {
SUB_CHALLENGE_SORT_TYPE_DEFAULT,
SUB_CHALLENGE_SORT_TYPE_CHALLENGEINDEX
}
public enum AllowAnimationType {
SUB_CHALLENGE_ANIM_TYPE_DEFAULT,
SUB_CHALLENGE_ANIM_TYPE_FORBID,
SUB_CHALLENGE_ANIM_TYPE_SUCCESS,
SUB_CHALLENGE_ANIM_TYPE_FAIL
}
}

View File

@@ -1,71 +1,71 @@
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.RewardPreviewData;
import emu.grasscutter.game.dungeons.enums.*;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "DungeonExcelConfigData.json")
public class DungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private int sceneId;
@Getter private int showLevel;
private DungeonType type;
private DungeonSubType subType;
private DungeonPlayType playType;
private DungeonInvolveType involveType;
@Getter private int limitLevel;
@Getter private int passCond;
@Getter private int reviveMaxCount;
@Getter private int settleCountdownTime;
@Getter private int failSettleCountdownTime;
@Getter private int quitSettleCountdownTime;
@Getter private List<SettleShowType> settleShows;
@Getter private int passRewardPreviewID;
@Getter private int statueCostID;
@Getter private int statueCostCount;
// not part of DungeonExcelConfigData
@Getter private RewardPreviewData rewardPreviewData;
public DungeonType getType() {
if (type == null) {
return DungeonType.DUNGEON_NONE;
}
return type;
}
public DungeonSubType getSubType() {
if (subType == null) {
return DungeonSubType.DUNGEON_SUB_NONE;
}
return subType;
}
public DungeonPlayType getPlayType() {
if (playType == null) {
return DungeonPlayType.DUNGEON_PLAY_TYPE_NONE;
}
return playType;
}
public DungeonInvolveType getInvolveType() {
if (involveType == null) {
return DungeonInvolveType.INVOLVE_NONE;
}
return involveType;
}
@Override
public void onLoad() {
if (this.passRewardPreviewID > 0) {
this.rewardPreviewData = GameData.getRewardPreviewDataMap().get(this.passRewardPreviewID);
}
}
}
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.RewardPreviewData;
import emu.grasscutter.game.dungeons.enums.*;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "DungeonExcelConfigData.json")
public class DungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@Getter private int sceneId;
@Getter private int showLevel;
private DungeonType type;
private DungeonSubType subType;
private DungeonPlayType playType;
private DungeonInvolveType involveType;
@Getter private int limitLevel;
@Getter private int passCond;
@Getter private int reviveMaxCount;
@Getter private int settleCountdownTime;
@Getter private int failSettleCountdownTime;
@Getter private int quitSettleCountdownTime;
@Getter private List<SettleShowType> settleShows;
@Getter private int passRewardPreviewID;
@Getter private int statueCostID;
@Getter private int statueCostCount;
// not part of DungeonExcelConfigData
@Getter private RewardPreviewData rewardPreviewData;
public DungeonType getType() {
if (type == null) {
return DungeonType.DUNGEON_NONE;
}
return type;
}
public DungeonSubType getSubType() {
if (subType == null) {
return DungeonSubType.DUNGEON_SUB_NONE;
}
return subType;
}
public DungeonPlayType getPlayType() {
if (playType == null) {
return DungeonPlayType.DUNGEON_PLAY_TYPE_NONE;
}
return playType;
}
public DungeonInvolveType getInvolveType() {
if (involveType == null) {
return DungeonInvolveType.INVOLVE_NONE;
}
return involveType;
}
@Override
public void onLoad() {
if (this.passRewardPreviewID > 0) {
this.rewardPreviewData = GameData.getRewardPreviewDataMap().get(this.passRewardPreviewID);
}
}
}

View File

@@ -1,17 +1,17 @@
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "DungeonEntryExcelConfigData.json")
@Getter
@Setter // TODO: remove this next API break
public class DungeonEntryData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int dungeonEntryId;
private int sceneId;
}
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "DungeonEntryExcelConfigData.json")
@Getter
@Setter // TODO: remove this next API break
public class DungeonEntryData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int dungeonEntryId;
private int sceneId;
}

View File

@@ -1,26 +1,26 @@
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.quest.enums.LogicType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "DungeonPassExcelConfigData.json")
public class DungeonPassConfigData extends GameResource {
@Getter private int id;
@Getter private LogicType logicType;
@Getter private List<DungeonPassCondition> conds;
public static class DungeonPassCondition {
@Getter private DungeonPassConditionType condType;
@Getter int[] param;
}
@Override
public void onLoad() {
super.onLoad();
conds = conds.stream().filter(condition -> condition.getCondType() != null).toList();
}
}
package emu.grasscutter.data.excels.dungeon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.quest.enums.LogicType;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "DungeonPassExcelConfigData.json")
public class DungeonPassConfigData extends GameResource {
@Getter private int id;
@Getter private LogicType logicType;
@Getter private List<DungeonPassCondition> conds;
public static class DungeonPassCondition {
@Getter private DungeonPassConditionType condType;
@Getter int[] param;
}
@Override
public void onLoad() {
super.onLoad();
conds = conds.stream().filter(condition -> condition.getCondType() != null).toList();
}
}

View File

@@ -1,32 +1,32 @@
package emu.grasscutter.data.excels.monster;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "MonsterCurveExcelConfigData.json")
public class MonsterCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap;
@Override
public int getId() {
return level;
}
public float getMultByProp(String fightProp) {
return curveInfoMap.getOrDefault(fightProp, 1f);
}
@Override
public void onLoad() {
this.curveInfoMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue()));
}
}
package emu.grasscutter.data.excels.monster;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "MonsterCurveExcelConfigData.json")
public class MonsterCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap;
@Override
public int getId() {
return level;
}
public float getMultByProp(String fightProp) {
return curveInfoMap.getOrDefault(fightProp, 1f);
}
@Override
public void onLoad() {
this.curveInfoMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue()));
}
}

View File

@@ -1,133 +1,133 @@
package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.MonsterType;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import lombok.Getter;
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter
public class MonsterData extends GameResource {
public static Set<FightProperty> definedFightProperties =
Set.of(
FightProperty.FIGHT_PROP_BASE_HP,
FightProperty.FIGHT_PROP_BASE_ATTACK,
FightProperty.FIGHT_PROP_BASE_DEFENSE,
FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT,
FightProperty.FIGHT_PROP_FIRE_SUB_HURT,
FightProperty.FIGHT_PROP_ELEC_SUB_HURT,
FightProperty.FIGHT_PROP_WATER_SUB_HURT,
FightProperty.FIGHT_PROP_GRASS_SUB_HURT,
FightProperty.FIGHT_PROP_WIND_SUB_HURT,
FightProperty.FIGHT_PROP_ROCK_SUB_HURT,
FightProperty.FIGHT_PROP_ICE_SUB_HURT);
@Getter(onMethod_ = @Override)
private int id;
private String monsterName;
private MonsterType type;
private String serverScript;
private List<Integer> affix;
private String ai;
private int[] equips;
private List<HpDrops> hpDrops;
private int killDropId;
private String excludeWeathers;
private int featureTagGroupID;
private int mpPropID;
private String skin;
private int describeId;
private int combatBGMLevel;
private int entityBudgetLevel;
@SerializedName("hpBase")
private float baseHp;
@SerializedName("attackBase")
private float baseAttack;
@SerializedName("defenseBase")
private float baseDefense;
private float fireSubHurt;
private float elecSubHurt;
private float grassSubHurt;
private float waterSubHurt;
private float windSubHurt;
private float rockSubHurt;
private float iceSubHurt;
private float physicalSubHurt;
private List<PropGrowCurve> propGrowCurves;
private long nameTextMapHash;
private int campID;
// Transient
private int weaponId;
private MonsterDescribeData describeData;
private int specialNameId; // will only be set if describe data is available
@Override
public void onLoad() {
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
if (this.describeData == null) {
return;
}
for (Entry<Integer, MonsterSpecialNameData> entry :
GameData.getMonsterSpecialNameDataMap().entrySet()) {
if (entry.getValue().getSpecialNameLabId() == this.getDescribeData().getSpecialNameLabId()) {
this.specialNameId = entry.getKey();
break;
}
}
}
public float getFightProperty(FightProperty prop) {
return switch (prop) {
case FIGHT_PROP_BASE_HP -> this.baseHp;
case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
default -> 0f;
};
}
@Getter
public class HpDrops {
private int DropId;
private int HpPercent;
}
}
package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.MonsterType;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import lombok.Getter;
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter
public class MonsterData extends GameResource {
public static Set<FightProperty> definedFightProperties =
Set.of(
FightProperty.FIGHT_PROP_BASE_HP,
FightProperty.FIGHT_PROP_BASE_ATTACK,
FightProperty.FIGHT_PROP_BASE_DEFENSE,
FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT,
FightProperty.FIGHT_PROP_FIRE_SUB_HURT,
FightProperty.FIGHT_PROP_ELEC_SUB_HURT,
FightProperty.FIGHT_PROP_WATER_SUB_HURT,
FightProperty.FIGHT_PROP_GRASS_SUB_HURT,
FightProperty.FIGHT_PROP_WIND_SUB_HURT,
FightProperty.FIGHT_PROP_ROCK_SUB_HURT,
FightProperty.FIGHT_PROP_ICE_SUB_HURT);
@Getter(onMethod_ = @Override)
private int id;
private String monsterName;
private MonsterType type;
private String serverScript;
private List<Integer> affix;
private String ai;
private int[] equips;
private List<HpDrops> hpDrops;
private int killDropId;
private String excludeWeathers;
private int featureTagGroupID;
private int mpPropID;
private String skin;
private int describeId;
private int combatBGMLevel;
private int entityBudgetLevel;
@SerializedName("hpBase")
private float baseHp;
@SerializedName("attackBase")
private float baseAttack;
@SerializedName("defenseBase")
private float baseDefense;
private float fireSubHurt;
private float elecSubHurt;
private float grassSubHurt;
private float waterSubHurt;
private float windSubHurt;
private float rockSubHurt;
private float iceSubHurt;
private float physicalSubHurt;
private List<PropGrowCurve> propGrowCurves;
private long nameTextMapHash;
private int campID;
// Transient
private int weaponId;
private MonsterDescribeData describeData;
private int specialNameId; // will only be set if describe data is available
@Override
public void onLoad() {
for (int id : this.equips) {
if (id == 0) {
continue;
}
GadgetData gadget = GameData.getGadgetDataMap().get(id);
if (gadget == null) {
continue;
}
if (gadget.getItemJsonName().equals("Default_MonsterWeapon")) {
this.weaponId = id;
}
}
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
if (this.describeData == null) {
return;
}
for (Entry<Integer, MonsterSpecialNameData> entry :
GameData.getMonsterSpecialNameDataMap().entrySet()) {
if (entry.getValue().getSpecialNameLabId() == this.getDescribeData().getSpecialNameLabId()) {
this.specialNameId = entry.getKey();
break;
}
}
}
public float getFightProperty(FightProperty prop) {
return switch (prop) {
case FIGHT_PROP_BASE_HP -> this.baseHp;
case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
default -> 0f;
};
}
@Getter
public class HpDrops {
private int DropId;
private int HpPercent;
}
}

View File

@@ -1,28 +1,28 @@
package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class MonsterDescribeData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private long nameTextMapHash;
@SerializedName(
value = "titleId",
alternate = {"titleID"})
private int titleId;
@SerializedName(
value = "specialNameLabId",
alternate = {"specialNameLabID"})
private int specialNameLabId;
private MonsterSpecialNameData specialNameData;
}
package emu.grasscutter.data.excels.monster;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class MonsterDescribeData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private long nameTextMapHash;
@SerializedName(
value = "titleId",
alternate = {"titleID"})
private int titleId;
@SerializedName(
value = "specialNameLabId",
alternate = {"specialNameLabID"})
private int specialNameLabId;
private MonsterSpecialNameData specialNameData;
}

View File

@@ -1,21 +1,21 @@
package emu.grasscutter.data.excels.monster;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ResourceType(name = "MonsterSpecialNameExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@EqualsAndHashCode(callSuper = false)
@Data
public class MonsterSpecialNameData extends GameResource {
private int specialNameId;
private int specialNameLabId;
private long specialNameTextMapHash;
@Override
public int getId() {
return specialNameId;
}
}
package emu.grasscutter.data.excels.monster;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ResourceType(name = "MonsterSpecialNameExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@EqualsAndHashCode(callSuper = false)
@Data
public class MonsterSpecialNameData extends GameResource {
private int specialNameId;
private int specialNameLabId;
private long specialNameTextMapHash;
@Override
public int getId() {
return specialNameId;
}
}

View File

@@ -1,24 +1,24 @@
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryAffixExcelConfigData.json")
@Getter
public class ReliquaryAffixData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int depotId;
private int groupId;
@SerializedName("propType")
private FightProperty fightProp;
private float propValue;
private int weight;
private int upgradeWeight;
}
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryAffixExcelConfigData.json")
@Getter
public class ReliquaryAffixData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int depotId;
private int groupId;
@SerializedName("propType")
private FightProperty fightProp;
private float propValue;
private int weight;
private int upgradeWeight;
}

View File

@@ -1,45 +1,45 @@
package emu.grasscutter.data.excels.reliquary;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "ReliquaryLevelExcelConfigData.json")
public class ReliquaryLevelData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private Int2FloatMap propMap;
@Getter private int rank;
@Getter private int level;
@Getter private int exp;
private List<RelicLevelProperty> addProps;
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());
}
}
@Getter
public class RelicLevelProperty {
private String propType;
private float value;
}
}
package emu.grasscutter.data.excels.reliquary;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "ReliquaryLevelExcelConfigData.json")
public class ReliquaryLevelData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private Int2FloatMap propMap;
@Getter private int rank;
@Getter private int level;
@Getter private int exp;
private List<RelicLevelProperty> addProps;
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());
}
}
@Getter
public class RelicLevelProperty {
private String propType;
private float value;
}
}

View File

@@ -1,21 +1,21 @@
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryMainPropExcelConfigData.json")
@Getter
public class ReliquaryMainPropData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int propDepotId;
@SerializedName("propType")
private FightProperty fightProp;
private int weight;
}
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryMainPropExcelConfigData.json")
@Getter
public class ReliquaryMainPropData extends GameResource {
@Getter(onMethod_ = @Override)
private int id;
private int propDepotId;
@SerializedName("propType")
private FightProperty fightProp;
private int weight;
}

View File

@@ -1,32 +1,32 @@
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "ReliquarySetExcelConfigData.json")
public class ReliquarySetData extends GameResource {
private int setId;
private int[] setNeedNum;
@SerializedName(
value = "equipAffixId",
alternate = {"EquipAffixId"})
private int equipAffixId;
@Override
public int getId() {
return setId;
}
public int[] getSetNeedNum() {
return setNeedNum;
}
public int getEquipAffixId() {
return equipAffixId;
}
@Override
public void onLoad() {}
}
package emu.grasscutter.data.excels.reliquary;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "ReliquarySetExcelConfigData.json")
public class ReliquarySetData extends GameResource {
private int setId;
private int[] setNeedNum;
@SerializedName(
value = "equipAffixId",
alternate = {"EquipAffixId"})
private int equipAffixId;
@Override
public int getId() {
return setId;
}
public int[] getSetNeedNum() {
return setNeedNum;
}
public int getEquipAffixId() {
return equipAffixId;
}
@Override
public void onLoad() {}
}

View File

@@ -1,21 +1,21 @@
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "TowerFloorExcelConfigData.json")
@Getter
public class TowerFloorData extends GameResource {
private int floorId;
private int floorIndex;
private int levelGroupId;
private int overrideMonsterLevel;
private int teamNum;
private int floorLevelConfigId;
@Override
public int getId() {
return this.floorId;
}
}
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "TowerFloorExcelConfigData.json")
@Getter
public class TowerFloorData extends GameResource {
private int floorId;
private int floorIndex;
private int levelGroupId;
private int overrideMonsterLevel;
private int teamNum;
private int floorLevelConfigId;
@Override
public int getId() {
return this.floorId;
}
}

View File

@@ -1,34 +1,34 @@
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "TowerLevelExcelConfigData.json")
public class TowerLevelData extends GameResource {
private int levelId;
private int levelIndex;
private int levelGroupId;
private int dungeonId;
@Override
public int getId() {
return this.getLevelId();
}
public int getLevelId() {
return levelId;
}
public int getLevelGroupId() {
return levelGroupId;
}
public int getLevelIndex() {
return levelIndex;
}
public int getDungeonId() {
return dungeonId;
}
}
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "TowerLevelExcelConfigData.json")
public class TowerLevelData extends GameResource {
private int levelId;
private int levelIndex;
private int levelGroupId;
private int dungeonId;
@Override
public int getId() {
return this.getLevelId();
}
public int getLevelId() {
return levelId;
}
public int getLevelGroupId() {
return levelGroupId;
}
public int getLevelIndex() {
return levelIndex;
}
public int getDungeonId() {
return dungeonId;
}
}

View File

@@ -1,49 +1,49 @@
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
@ResourceType(name = "TowerScheduleExcelConfigData.json")
public class TowerScheduleData extends GameResource {
private int scheduleId;
private List<Integer> entranceFloorId;
private List<ScheduleDetail> schedules;
private int monthlyLevelConfigId;
@Override
public int getId() {
return scheduleId;
}
@Override
public void onLoad() {
super.onLoad();
this.schedules =
this.schedules.stream().filter(item -> item.getFloorList().size() > 0).toList();
}
public int getScheduleId() {
return scheduleId;
}
public List<Integer> getEntranceFloorId() {
return entranceFloorId;
}
public List<ScheduleDetail> getSchedules() {
return schedules;
}
public int getMonthlyLevelConfigId() {
return monthlyLevelConfigId;
}
public static class ScheduleDetail {
private List<Integer> floorList;
public List<Integer> getFloorList() {
return floorList;
}
}
}
package emu.grasscutter.data.excels.tower;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
@ResourceType(name = "TowerScheduleExcelConfigData.json")
public class TowerScheduleData extends GameResource {
private int scheduleId;
private List<Integer> entranceFloorId;
private List<ScheduleDetail> schedules;
private int monthlyLevelConfigId;
@Override
public int getId() {
return scheduleId;
}
@Override
public void onLoad() {
super.onLoad();
this.schedules =
this.schedules.stream().filter(item -> item.getFloorList().size() > 0).toList();
}
public int getScheduleId() {
return scheduleId;
}
public List<Integer> getEntranceFloorId() {
return entranceFloorId;
}
public List<ScheduleDetail> getSchedules() {
return schedules;
}
public int getMonthlyLevelConfigId() {
return monthlyLevelConfigId;
}
public static class ScheduleDetail {
private List<Integer> floorList;
public List<Integer> getFloorList() {
return floorList;
}
}
}

View File

@@ -1,21 +1,21 @@
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.BaseTrialActivityData;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarActivityExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarActivityData extends GameResource implements BaseTrialActivityData {
private int ScheduleId;
private List<Integer> AvatarIndexIdList;
private List<Integer> RewardIdList;
@Override
public int getId() {
return ScheduleId;
}
}
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.BaseTrialActivityData;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarActivityExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarActivityData extends GameResource implements BaseTrialActivityData {
private int ScheduleId;
private List<Integer> AvatarIndexIdList;
private List<Integer> RewardIdList;
@Override
public int getId() {
return ScheduleId;
}
}

View File

@@ -1,31 +1,31 @@
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "TrialAvatarActivityDataExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TrialAvatarActivityDataData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int trialAvatarIndexId;
private int trialAvatarId;
private int dungeonId;
private String battleAvatarsList;
private int firstPassReward;
private ActivityWatcherData.WatcherTrigger triggerConfig;
private int progress;
@Override
public void onLoad() {
this.triggerConfig.onLoad();
GameData.getTrialAvatarIndexIdTrialActivityDataDataMap().put(trialAvatarIndexId, id);
}
}
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "TrialAvatarActivityDataExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class TrialAvatarActivityDataData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int trialAvatarIndexId;
private int trialAvatarId;
private int dungeonId;
private String battleAvatarsList;
private int firstPassReward;
private ActivityWatcherData.WatcherTrigger triggerConfig;
private int progress;
@Override
public void onLoad() {
this.triggerConfig.onLoad();
GameData.getTrialAvatarIndexIdTrialActivityDataDataMap().put(trialAvatarIndexId, id);
}
}

View File

@@ -1,19 +1,19 @@
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarData extends GameResource {
private int trialAvatarId;
private List<Integer> trialAvatarParamList;
@Override
public int getId() {
return trialAvatarId;
}
}
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarData extends GameResource {
private int trialAvatarId;
private List<Integer> trialAvatarParamList;
@Override
public int getId() {
return trialAvatarId;
}
}

View File

@@ -1,20 +1,20 @@
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarTemplateExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarTemplateData extends GameResource {
private int TrialAvatarLevel;
private List<Integer> TrialReliquaryList;
private int TrialAvatarSkillLevel;
@Override
public int getId() {
return TrialAvatarLevel;
}
}
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialAvatarTemplateExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialAvatarTemplateData extends GameResource {
private int TrialAvatarLevel;
private List<Integer> TrialReliquaryList;
private int TrialAvatarSkillLevel;
@Override
public int getId() {
return TrialAvatarLevel;
}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialReliquaryExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialReliquaryData extends GameResource {
private int Id;
private int ReliquaryId;
private int Level;
private int MainPropId;
private List<Integer> AppendPropList;
@Override
public int getId() {
return Id;
}
}
package emu.grasscutter.data.excels.trial;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import java.util.List;
import lombok.*;
@ResourceType(name = "TrialReliquaryExcelConfigData.json")
@EqualsAndHashCode(callSuper = false)
@Data
public class TrialReliquaryData extends GameResource {
private int Id;
private int ReliquaryId;
private int Level;
private int MainPropId;
private List<Integer> AppendPropList;
@Override
public int getId() {
return Id;
}
}

View File

@@ -1,32 +1,32 @@
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "WeaponCurveExcelConfigData.json")
public class WeaponCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfosMap;
@Override
public int getId() {
return level;
}
public float getMultByProp(String fightProp) {
return curveInfosMap.getOrDefault(fightProp, 1f);
}
@Override
public void onLoad() {
this.curveInfosMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfosMap.put(info.getType(), info.getValue()));
}
}
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
@ResourceType(name = "WeaponCurveExcelConfigData.json")
public class WeaponCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfosMap;
@Override
public int getId() {
return level;
}
public float getMultByProp(String fightProp) {
return curveInfosMap.getOrDefault(fightProp, 1f);
}
@Override
public void onLoad() {
this.curveInfosMap = new HashMap<>();
Stream.of(this.curveInfos)
.forEach(info -> this.curveInfosMap.put(info.getType(), info.getValue()));
}
}

View File

@@ -1,23 +1,23 @@
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WeaponLevelExcelConfigData.json")
public class WeaponLevelData extends GameResource {
private int level;
private int[] requiredExps;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public int[] getRequiredExps() {
return requiredExps;
}
}
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WeaponLevelExcelConfigData.json")
public class WeaponLevelData extends GameResource {
private int level;
private int[] requiredExps;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public int[] getRequiredExps() {
return requiredExps;
}
}

View File

@@ -1,74 +1,74 @@
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData;
import java.util.ArrayList;
@ResourceType(name = "WeaponPromoteExcelConfigData.json")
public class WeaponPromoteData extends GameResource {
private int weaponPromoteId;
private int promoteLevel;
private ItemParamData[] costItems;
private int coinCost;
private FightPropData[] addProps;
private int unlockMaxLevel;
private int requiredPlayerLevel;
@Override
public int getId() {
return (weaponPromoteId << 8) + promoteLevel;
}
public int getWeaponPromoteId() {
return weaponPromoteId;
}
public int getPromoteLevel() {
return promoteLevel;
}
public ItemParamData[] getCostItems() {
return costItems;
}
public int getCoinCost() {
return coinCost;
}
public FightPropData[] getAddProps() {
return addProps;
}
public int getUnlockMaxLevel() {
return unlockMaxLevel;
}
public int getRequiredPlayerLevel() {
return requiredPlayerLevel;
}
@Override
public void onLoad() {
// Trim item params
ArrayList<ItemParamData> trim = new ArrayList<>(getAddProps().length);
for (ItemParamData itemParam : getCostItems()) {
if (itemParam.getId() == 0) {
continue;
}
trim.add(itemParam);
}
this.costItems = trim.toArray(new ItemParamData[trim.size()]);
// Trim fight prop data
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}
package emu.grasscutter.data.excels.weapon;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.FightPropData;
import emu.grasscutter.data.common.ItemParamData;
import java.util.ArrayList;
@ResourceType(name = "WeaponPromoteExcelConfigData.json")
public class WeaponPromoteData extends GameResource {
private int weaponPromoteId;
private int promoteLevel;
private ItemParamData[] costItems;
private int coinCost;
private FightPropData[] addProps;
private int unlockMaxLevel;
private int requiredPlayerLevel;
@Override
public int getId() {
return (weaponPromoteId << 8) + promoteLevel;
}
public int getWeaponPromoteId() {
return weaponPromoteId;
}
public int getPromoteLevel() {
return promoteLevel;
}
public ItemParamData[] getCostItems() {
return costItems;
}
public int getCoinCost() {
return coinCost;
}
public FightPropData[] getAddProps() {
return addProps;
}
public int getUnlockMaxLevel() {
return unlockMaxLevel;
}
public int getRequiredPlayerLevel() {
return requiredPlayerLevel;
}
@Override
public void onLoad() {
// Trim item params
ArrayList<ItemParamData> trim = new ArrayList<>(getAddProps().length);
for (ItemParamData itemParam : getCostItems()) {
if (itemParam.getId() == 0) {
continue;
}
trim.add(itemParam);
}
this.costItems = trim.toArray(new ItemParamData[trim.size()]);
// Trim fight prop data
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length);
for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad();
parsed.add(prop);
}
}
this.addProps = parsed.toArray(new FightPropData[parsed.size()]);
}
}

View File

@@ -1,26 +1,26 @@
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ClimateType;
import lombok.Getter;
@ResourceType(name = "WeatherExcelConfigData.json")
public class WeatherData extends GameResource {
@Getter private int areaID;
@Getter private int weatherAreaId;
@Getter private String maxHeightStr;
@Getter private int gadgetID;
@Getter private boolean isDefaultValid;
@Getter private String templateName;
@Getter private int priority;
@Getter private String profileName;
@Getter private ClimateType defaultClimate;
@Getter private boolean isUseDefault;
@Getter private int sceneID;
@Override
public int getId() {
return this.areaID;
}
}
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ClimateType;
import lombok.Getter;
@ResourceType(name = "WeatherExcelConfigData.json")
public class WeatherData extends GameResource {
@Getter private int areaID;
@Getter private int weatherAreaId;
@Getter private String maxHeightStr;
@Getter private int gadgetID;
@Getter private boolean isDefaultValid;
@Getter private String templateName;
@Getter private int priority;
@Getter private String profileName;
@Getter private ClimateType defaultClimate;
@Getter private boolean isUseDefault;
@Getter private int sceneID;
@Override
public int getId() {
return this.areaID;
}
}

View File

@@ -1,30 +1,30 @@
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ElementType;
@ResourceType(name = "WorldAreaConfigData.json")
public class WorldAreaData extends GameResource {
private int ID;
private int AreaID1;
private int AreaID2;
private int SceneID;
private ElementType elementType;
@Override
public int getId() {
return (this.AreaID2 << 16) + this.AreaID1;
}
public int getSceneID() {
return this.SceneID;
}
public ElementType getElementType() {
return this.elementType;
}
@Override
public void onLoad() {}
}
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ElementType;
@ResourceType(name = "WorldAreaConfigData.json")
public class WorldAreaData extends GameResource {
private int ID;
private int AreaID1;
private int AreaID2;
private int SceneID;
private ElementType elementType;
@Override
public int getId() {
return (this.AreaID2 << 16) + this.AreaID1;
}
public int getSceneID() {
return this.SceneID;
}
public ElementType getElementType() {
return this.elementType;
}
@Override
public void onLoad() {}
}

View File

@@ -1,22 +1,22 @@
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WorldLevelExcelConfigData.json")
public class WorldLevelData extends GameResource {
private int level;
private int monsterLevel;
@Override
public int getId() {
return this.level;
}
public int getMonsterLevel() {
return monsterLevel;
}
@Override
public void onLoad() {}
}
package emu.grasscutter.data.excels.world;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WorldLevelExcelConfigData.json")
public class WorldLevelData extends GameResource {
private int level;
private int monsterLevel;
@Override
public int getId() {
return this.level;
}
public int getMonsterLevel() {
return monsterLevel;
}
@Override
public void onLoad() {}
}

View File

@@ -1,10 +1,10 @@
package emu.grasscutter.data.server;
import java.util.List;
import lombok.Data;
@Data
public class ActivityCondGroup {
int condGroupId;
List<Integer> condIds;
}
package emu.grasscutter.data.server;
import java.util.List;
import lombok.Data;
@Data
public class ActivityCondGroup {
int condGroupId;
List<Integer> condIds;
}

View File

@@ -1,9 +1,9 @@
package emu.grasscutter.data.server;
import lombok.Data;
@Data
public class GadgetMapping {
private int gadgetId;
private String serverController;
}
package emu.grasscutter.data.server;
import lombok.Data;
@Data
public class GadgetMapping {
private int gadgetId;
private String serverController;
}

View File

@@ -1,34 +1,34 @@
package emu.grasscutter.data.server;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.GridPosition;
import emu.grasscutter.utils.Position;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Grid {
public Map<GridPosition, Set<Integer>> grid;
public Set<Integer> getNearbyGroups(int vision_level, Position position) {
int width = Grasscutter.getConfig().server.game.visionOptions[vision_level].gridWidth;
int vision_range = Grasscutter.getConfig().server.game.visionOptions[vision_level].visionRange;
int vision_range_grid = vision_range / width;
GridPosition pos = new GridPosition(position, width);
Set<Integer> nearbyGroups = new HashSet<>();
// construct a nearby pisition list, add 1 more because a player can be in an edge case, this
// should not affect much the loading
for (int x = 0; x < vision_range_grid + 1; x++) {
for (int z = 0; z < vision_range_grid + 1; z++) {
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(x, z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(x, -z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, -z), new HashSet<>()));
}
}
return nearbyGroups;
}
}
package emu.grasscutter.data.server;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.GridPosition;
import emu.grasscutter.utils.Position;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Grid {
public Map<GridPosition, Set<Integer>> grid;
public Set<Integer> getNearbyGroups(int vision_level, Position position) {
int width = Grasscutter.getConfig().server.game.visionOptions[vision_level].gridWidth;
int vision_range = Grasscutter.getConfig().server.game.visionOptions[vision_level].visionRange;
int vision_range_grid = vision_range / width;
GridPosition pos = new GridPosition(position, width);
Set<Integer> nearbyGroups = new HashSet<>();
// construct a nearby pisition list, add 1 more because a player can be in an edge case, this
// should not affect much the loading
for (int x = 0; x < vision_range_grid + 1; x++) {
for (int z = 0; z < vision_range_grid + 1; z++) {
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(x, z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(x, -z), new HashSet<>()));
nearbyGroups.addAll(grid.getOrDefault(pos.addClone(-x, -z), new HashSet<>()));
}
}
return nearbyGroups;
}
}

View File

@@ -1,464 +1,464 @@
package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq;
import com.mongodb.client.result.DeleteResult;
import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort;
import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.battlepass.BattlePassManager;
import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List;
import java.util.stream.Stream;
public final class DatabaseHelper {
public static Account createAccount(String username) {
return createAccountWithUid(username, 0);
}
public static Account createAccountWithUid(String username, int reservedUid) {
// Unique names only
if (DatabaseHelper.checkIfAccountExists(username)) {
return null;
}
// Make sure there are no id collisions
if (reservedUid > 0) {
// Cannot make account with the same uid as the server console
if (reservedUid == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
if (DatabaseHelper.checkIfAccountExists(reservedUid)) {
return null;
}
// Make sure no existing player already has this id.
if (DatabaseHelper.checkIfPlayerExists(reservedUid)) {
return null;
}
}
// Account
Account account = new Account();
account.setUsername(username);
account.setId(Integer.toString(DatabaseManager.getNextId(account)));
if (reservedUid > 0) {
account.setReservedPlayerUid(reservedUid);
}
DatabaseHelper.saveAccount(account);
return account;
}
@Deprecated
public static Account createAccountWithPassword(String username, String password) {
// Unique names only
Account exists = DatabaseHelper.getAccountByName(username);
if (exists != null) {
return null;
}
// Account
Account account = new Account();
account.setId(Integer.toString(DatabaseManager.getNextId(account)));
account.setUsername(username);
account.setPassword(password);
DatabaseHelper.saveAccount(account);
return account;
}
public static void saveAccount(Account account) {
DatabaseManager.getAccountDatastore().save(account);
}
public static Account getAccountByName(String username) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("username", username))
.first();
}
public static Account getAccountByToken(String token) {
if (token == null) return null;
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("token", token))
.first();
}
public static Account getAccountBySessionKey(String sessionKey) {
if (sessionKey == null) return null;
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("sessionKey", sessionKey))
.first();
}
public static Account getAccountById(String uid) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("_id", uid))
.first();
}
public static Account getAccountByPlayerId(int playerId) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("reservedPlayerId", playerId))
.first();
}
public static boolean checkIfAccountExists(String name) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("username", name))
.count()
> 0;
}
public static boolean checkIfAccountExists(int reservedUid) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("reservedPlayerId", reservedUid))
.count()
> 0;
}
public static synchronized void deleteAccount(Account target) {
// To delete an account, we need to also delete all the other documents in the database that
// reference the account.
// This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way
// does not leave the
// database in an inconsistent state, but unfortunately Mongo only supports that when we have a
// replica set ...
Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId());
// Close session first
if (player != null) {
player.getSession().close();
} else {
player = getPlayerByAccount(target);
if (player == null) return;
}
int uid = player.getUid();
// Delete data from collections
DatabaseManager.getGameDatabase().getCollection("achievements").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("activities").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("homes").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("battlepass").deleteMany(eq("ownerUid", uid));
// Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships,
// but also all friendship entries for that account's friends.
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", uid));
// Delete the player last.
DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", uid)).delete();
// Finally, delete the account itself.
DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("id", target.getId()))
.delete();
}
public static <T> Stream<T> getByGameClass(Class<T> classType) {
return DatabaseManager.getGameDatastore().find(classType).stream();
}
@Deprecated(forRemoval = true)
public static List<Player> getAllPlayers() {
return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
}
public static Player getPlayerByUid(int id) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("_id", id))
.first();
}
@Deprecated
public static Player getPlayerByAccount(Account account) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("accountId", account.getId()))
.first();
}
public static Player getPlayerByAccount(Account account, Class<? extends Player> playerClass) {
return DatabaseManager.getGameDatastore()
.find(playerClass)
.filter(Filters.eq("accountId", account.getId()))
.first();
}
public static boolean checkIfPlayerExists(int uid) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("_id", uid))
.count()
> 0;
}
public static synchronized Player generatePlayerUid(Player character, int reservedId) {
// Check if reserved id
int id;
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId;
character.setUid(id);
} else {
do {
id = DatabaseManager.getNextId(character);
} while (checkIfPlayerExists(id));
character.setUid(id);
}
// Save to database
DatabaseManager.getGameDatastore().save(character);
return character;
}
public static synchronized int getNextPlayerId(int reservedId) {
// Check if reserved id
int id;
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId;
} else {
do {
id = DatabaseManager.getNextId(Player.class);
} while (checkIfPlayerExists(id));
}
return id;
}
public static void savePlayer(Player character) {
DatabaseManager.getGameDatastore().save(character);
}
public static void saveAvatar(Avatar avatar) {
DatabaseManager.getGameDatastore().save(avatar);
}
public static List<Avatar> getAvatars(Player player) {
return DatabaseManager.getGameDatastore()
.find(Avatar.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static void saveItem(GameItem item) {
DatabaseManager.getGameDatastore().save(item);
}
public static boolean deleteItem(GameItem item) {
DeleteResult result = DatabaseManager.getGameDatastore().delete(item);
return result.wasAcknowledged();
}
public static List<GameItem> getInventoryItems(Player player) {
return DatabaseManager.getGameDatastore()
.find(GameItem.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static List<Friendship> getFriends(Player player) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static List<Friendship> getReverseFriends(Player player) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(Filters.eq("friendId", player.getUid()))
.stream()
.toList();
}
public static void saveFriendship(Friendship friendship) {
DatabaseManager.getGameDatastore().save(friendship);
}
public static void deleteFriendship(Friendship friendship) {
DatabaseManager.getGameDatastore().delete(friendship);
}
public static Friendship getReverseFriendship(Friendship friendship) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(
Filters.and(
Filters.eq("ownerId", friendship.getFriendId()),
Filters.eq("friendId", friendship.getOwnerId())))
.first();
}
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType) {
return getGachaRecords(ownerId, page, gachaType, 10);
}
public static List<GachaRecord> getGachaRecords(
int ownerId, int page, int gachaType, int pageSize) {
return DatabaseManager.getGameDatastore()
.find(GachaRecord.class)
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.iterator(
new FindOptions()
.sort(Sort.descending("transactionDate"))
.skip(pageSize * page)
.limit(pageSize))
.toList();
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType) {
return getGachaRecordsMaxPage(ownerId, page, gachaType, 10);
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize) {
long count =
DatabaseManager.getGameDatastore()
.find(GachaRecord.class)
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.count();
return count / 10 + (count % 10 > 0 ? 1 : 0);
}
public static void saveGachaRecord(GachaRecord gachaRecord) {
DatabaseManager.getGameDatastore().save(gachaRecord);
}
public static List<Mail> getAllMail(Player player) {
return DatabaseManager.getGameDatastore()
.find(Mail.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.stream()
.toList();
}
public static void saveMail(Mail mail) {
DatabaseManager.getGameDatastore().save(mail);
}
public static boolean deleteMail(Mail mail) {
DeleteResult result = DatabaseManager.getGameDatastore().delete(mail);
return result.wasAcknowledged();
}
public static List<GameMainQuest> getAllQuests(Player player) {
return DatabaseManager.getGameDatastore()
.find(GameMainQuest.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.stream()
.toList();
}
public static void saveQuest(GameMainQuest quest) {
DatabaseManager.getGameDatastore().save(quest);
}
public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
}
public static GameHome getHomeByUid(int id) {
return DatabaseManager.getGameDatastore()
.find(GameHome.class)
.filter(Filters.eq("ownerUid", id))
.first();
}
public static void saveHome(GameHome gameHome) {
DatabaseManager.getGameDatastore().save(gameHome);
}
public static BattlePassManager loadBattlePass(Player player) {
BattlePassManager manager =
DatabaseManager.getGameDatastore()
.find(BattlePassManager.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.first();
if (manager == null) {
manager = new BattlePassManager(player);
manager.save();
} else {
manager.setPlayer(player);
}
return manager;
}
public static void saveBattlePass(BattlePassManager manager) {
DatabaseManager.getGameDatastore().save(manager);
}
public static PlayerActivityData getPlayerActivityData(int uid, int activityId) {
return DatabaseManager.getGameDatastore()
.find(PlayerActivityData.class)
.filter(Filters.and(Filters.eq("uid", uid), Filters.eq("activityId", activityId)))
.first();
}
public static void savePlayerActivityData(PlayerActivityData playerActivityData) {
DatabaseManager.getGameDatastore().save(playerActivityData);
}
public static MusicGameBeatmap getMusicGameBeatmap(long musicShareId) {
return DatabaseManager.getGameDatastore()
.find(MusicGameBeatmap.class)
.filter(Filters.eq("musicShareId", musicShareId))
.first();
}
public static void saveMusicGameBeatmap(MusicGameBeatmap musicGameBeatmap) {
DatabaseManager.getGameDatastore().save(musicGameBeatmap);
}
public static Achievements getAchievementData(int uid) {
return DatabaseManager.getGameDatastore()
.find(Achievements.class)
.filter(Filters.and(Filters.eq("uid", uid)))
.first();
}
public static void saveAchievementData(Achievements achievements) {
DatabaseManager.getGameDatastore().save(achievements);
}
public static void saveGroupInstance(SceneGroupInstance instance) {
DatabaseManager.getGameDatastore().save(instance);
}
public static SceneGroupInstance loadGroupInstance(int groupId, Player owner) {
return DatabaseManager.getGameDatastore()
.find(SceneGroupInstance.class)
.filter(Filters.and(Filters.eq("ownerUid", owner.getUid()), Filters.eq("groupId", groupId)))
.first();
}
}
package emu.grasscutter.database;
import static com.mongodb.client.model.Filters.eq;
import com.mongodb.client.result.DeleteResult;
import dev.morphia.query.FindOptions;
import dev.morphia.query.Sort;
import dev.morphia.query.experimental.filters.Filters;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.achievement.Achievements;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.musicgame.MusicGameBeatmap;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.battlepass.BattlePassManager;
import emu.grasscutter.game.friends.Friendship;
import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.home.GameHome;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.world.SceneGroupInstance;
import java.util.List;
import java.util.stream.Stream;
public final class DatabaseHelper {
public static Account createAccount(String username) {
return createAccountWithUid(username, 0);
}
public static Account createAccountWithUid(String username, int reservedUid) {
// Unique names only
if (DatabaseHelper.checkIfAccountExists(username)) {
return null;
}
// Make sure there are no id collisions
if (reservedUid > 0) {
// Cannot make account with the same uid as the server console
if (reservedUid == GameConstants.SERVER_CONSOLE_UID) {
return null;
}
if (DatabaseHelper.checkIfAccountExists(reservedUid)) {
return null;
}
// Make sure no existing player already has this id.
if (DatabaseHelper.checkIfPlayerExists(reservedUid)) {
return null;
}
}
// Account
Account account = new Account();
account.setUsername(username);
account.setId(Integer.toString(DatabaseManager.getNextId(account)));
if (reservedUid > 0) {
account.setReservedPlayerUid(reservedUid);
}
DatabaseHelper.saveAccount(account);
return account;
}
@Deprecated
public static Account createAccountWithPassword(String username, String password) {
// Unique names only
Account exists = DatabaseHelper.getAccountByName(username);
if (exists != null) {
return null;
}
// Account
Account account = new Account();
account.setId(Integer.toString(DatabaseManager.getNextId(account)));
account.setUsername(username);
account.setPassword(password);
DatabaseHelper.saveAccount(account);
return account;
}
public static void saveAccount(Account account) {
DatabaseManager.getAccountDatastore().save(account);
}
public static Account getAccountByName(String username) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("username", username))
.first();
}
public static Account getAccountByToken(String token) {
if (token == null) return null;
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("token", token))
.first();
}
public static Account getAccountBySessionKey(String sessionKey) {
if (sessionKey == null) return null;
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("sessionKey", sessionKey))
.first();
}
public static Account getAccountById(String uid) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("_id", uid))
.first();
}
public static Account getAccountByPlayerId(int playerId) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("reservedPlayerId", playerId))
.first();
}
public static boolean checkIfAccountExists(String name) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("username", name))
.count()
> 0;
}
public static boolean checkIfAccountExists(int reservedUid) {
return DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("reservedPlayerId", reservedUid))
.count()
> 0;
}
public static synchronized void deleteAccount(Account target) {
// To delete an account, we need to also delete all the other documents in the database that
// reference the account.
// This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way
// does not leave the
// database in an inconsistent state, but unfortunately Mongo only supports that when we have a
// replica set ...
Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId());
// Close session first
if (player != null) {
player.getSession().close();
} else {
player = getPlayerByAccount(target);
if (player == null) return;
}
int uid = player.getUid();
// Delete data from collections
DatabaseManager.getGameDatabase().getCollection("achievements").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("activities").deleteMany(eq("uid", uid));
DatabaseManager.getGameDatabase().getCollection("homes").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", uid));
DatabaseManager.getGameDatabase().getCollection("battlepass").deleteMany(eq("ownerUid", uid));
// Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships,
// but also all friendship entries for that account's friends.
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", uid));
DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", uid));
// Delete the player last.
DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", uid)).delete();
// Finally, delete the account itself.
DatabaseManager.getAccountDatastore()
.find(Account.class)
.filter(Filters.eq("id", target.getId()))
.delete();
}
public static <T> Stream<T> getByGameClass(Class<T> classType) {
return DatabaseManager.getGameDatastore().find(classType).stream();
}
@Deprecated(forRemoval = true)
public static List<Player> getAllPlayers() {
return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
}
public static Player getPlayerByUid(int id) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("_id", id))
.first();
}
@Deprecated
public static Player getPlayerByAccount(Account account) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("accountId", account.getId()))
.first();
}
public static Player getPlayerByAccount(Account account, Class<? extends Player> playerClass) {
return DatabaseManager.getGameDatastore()
.find(playerClass)
.filter(Filters.eq("accountId", account.getId()))
.first();
}
public static boolean checkIfPlayerExists(int uid) {
return DatabaseManager.getGameDatastore()
.find(Player.class)
.filter(Filters.eq("_id", uid))
.count()
> 0;
}
public static synchronized Player generatePlayerUid(Player character, int reservedId) {
// Check if reserved id
int id;
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId;
character.setUid(id);
} else {
do {
id = DatabaseManager.getNextId(character);
} while (checkIfPlayerExists(id));
character.setUid(id);
}
// Save to database
DatabaseManager.getGameDatastore().save(character);
return character;
}
public static synchronized int getNextPlayerId(int reservedId) {
// Check if reserved id
int id;
if (reservedId > 0 && !checkIfPlayerExists(reservedId)) {
id = reservedId;
} else {
do {
id = DatabaseManager.getNextId(Player.class);
} while (checkIfPlayerExists(id));
}
return id;
}
public static void savePlayer(Player character) {
DatabaseManager.getGameDatastore().save(character);
}
public static void saveAvatar(Avatar avatar) {
DatabaseManager.getGameDatastore().save(avatar);
}
public static List<Avatar> getAvatars(Player player) {
return DatabaseManager.getGameDatastore()
.find(Avatar.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static void saveItem(GameItem item) {
DatabaseManager.getGameDatastore().save(item);
}
public static boolean deleteItem(GameItem item) {
DeleteResult result = DatabaseManager.getGameDatastore().delete(item);
return result.wasAcknowledged();
}
public static List<GameItem> getInventoryItems(Player player) {
return DatabaseManager.getGameDatastore()
.find(GameItem.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static List<Friendship> getFriends(Player player) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(Filters.eq("ownerId", player.getUid()))
.stream()
.toList();
}
public static List<Friendship> getReverseFriends(Player player) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(Filters.eq("friendId", player.getUid()))
.stream()
.toList();
}
public static void saveFriendship(Friendship friendship) {
DatabaseManager.getGameDatastore().save(friendship);
}
public static void deleteFriendship(Friendship friendship) {
DatabaseManager.getGameDatastore().delete(friendship);
}
public static Friendship getReverseFriendship(Friendship friendship) {
return DatabaseManager.getGameDatastore()
.find(Friendship.class)
.filter(
Filters.and(
Filters.eq("ownerId", friendship.getFriendId()),
Filters.eq("friendId", friendship.getOwnerId())))
.first();
}
public static List<GachaRecord> getGachaRecords(int ownerId, int page, int gachaType) {
return getGachaRecords(ownerId, page, gachaType, 10);
}
public static List<GachaRecord> getGachaRecords(
int ownerId, int page, int gachaType, int pageSize) {
return DatabaseManager.getGameDatastore()
.find(GachaRecord.class)
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.iterator(
new FindOptions()
.sort(Sort.descending("transactionDate"))
.skip(pageSize * page)
.limit(pageSize))
.toList();
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType) {
return getGachaRecordsMaxPage(ownerId, page, gachaType, 10);
}
public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize) {
long count =
DatabaseManager.getGameDatastore()
.find(GachaRecord.class)
.filter(Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType))
.count();
return count / 10 + (count % 10 > 0 ? 1 : 0);
}
public static void saveGachaRecord(GachaRecord gachaRecord) {
DatabaseManager.getGameDatastore().save(gachaRecord);
}
public static List<Mail> getAllMail(Player player) {
return DatabaseManager.getGameDatastore()
.find(Mail.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.stream()
.toList();
}
public static void saveMail(Mail mail) {
DatabaseManager.getGameDatastore().save(mail);
}
public static boolean deleteMail(Mail mail) {
DeleteResult result = DatabaseManager.getGameDatastore().delete(mail);
return result.wasAcknowledged();
}
public static List<GameMainQuest> getAllQuests(Player player) {
return DatabaseManager.getGameDatastore()
.find(GameMainQuest.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.stream()
.toList();
}
public static void saveQuest(GameMainQuest quest) {
DatabaseManager.getGameDatastore().save(quest);
}
public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged();
}
public static GameHome getHomeByUid(int id) {
return DatabaseManager.getGameDatastore()
.find(GameHome.class)
.filter(Filters.eq("ownerUid", id))
.first();
}
public static void saveHome(GameHome gameHome) {
DatabaseManager.getGameDatastore().save(gameHome);
}
public static BattlePassManager loadBattlePass(Player player) {
BattlePassManager manager =
DatabaseManager.getGameDatastore()
.find(BattlePassManager.class)
.filter(Filters.eq("ownerUid", player.getUid()))
.first();
if (manager == null) {
manager = new BattlePassManager(player);
manager.save();
} else {
manager.setPlayer(player);
}
return manager;
}
public static void saveBattlePass(BattlePassManager manager) {
DatabaseManager.getGameDatastore().save(manager);
}
public static PlayerActivityData getPlayerActivityData(int uid, int activityId) {
return DatabaseManager.getGameDatastore()
.find(PlayerActivityData.class)
.filter(Filters.and(Filters.eq("uid", uid), Filters.eq("activityId", activityId)))
.first();
}
public static void savePlayerActivityData(PlayerActivityData playerActivityData) {
DatabaseManager.getGameDatastore().save(playerActivityData);
}
public static MusicGameBeatmap getMusicGameBeatmap(long musicShareId) {
return DatabaseManager.getGameDatastore()
.find(MusicGameBeatmap.class)
.filter(Filters.eq("musicShareId", musicShareId))
.first();
}
public static void saveMusicGameBeatmap(MusicGameBeatmap musicGameBeatmap) {
DatabaseManager.getGameDatastore().save(musicGameBeatmap);
}
public static Achievements getAchievementData(int uid) {
return DatabaseManager.getGameDatastore()
.find(Achievements.class)
.filter(Filters.and(Filters.eq("uid", uid)))
.first();
}
public static void saveAchievementData(Achievements achievements) {
DatabaseManager.getGameDatastore().save(achievements);
}
public static void saveGroupInstance(SceneGroupInstance instance) {
DatabaseManager.getGameDatastore().save(instance);
}
public static SceneGroupInstance loadGroupInstance(int groupId, Player owner) {
return DatabaseManager.getGameDatastore()
.find(SceneGroupInstance.class)
.filter(Filters.and(Filters.eq("ownerUid", owner.getUid()), Filters.eq("groupId", groupId)))
.first();
}
}

View File

@@ -1,246 +1,246 @@
package emu.grasscutter.game.ability;
import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetGatherObject;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap;
import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina;
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import lombok.Getter;
public final class AbilityManager extends BasePlayerManager {
HealAbilityManager healAbilityManager;
@Getter private boolean abilityInvulnerable = false;
public AbilityManager(Player player) {
super(player);
this.healAbilityManager = new HealAbilityManager(player);
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
this.healAbilityManager.healHandler(invoke);
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue()
// + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM -> this.handleOverrideParam(invoke);
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP -> this.handleReinitOverrideMap(invoke);
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE -> this.handleModifierChange(invoke);
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA -> this.handleMixinCostStamina(invoke);
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL -> this.handleGenerateElemBall(invoke);
case ABILITY_INVOKE_ARGUMENT_META_GLOBAL_FLOAT_VALUE -> this.handleGlobalFloatValue(invoke);
default -> {}
}
}
/**
* Invoked when a player starts a skill.
*
* @param player The player who started the skill.
* @param skillId The skill ID.
* @param casterId The caster ID.
*/
public void onSkillStart(Player player, int skillId, int casterId) {
// Check if the player matches this player.
if (player.getUid() != this.player.getUid()) {
return;
}
// Check if the caster matches the player.
if (player.getTeamManager().getCurrentAvatarEntity().getId() != casterId) {
return;
}
var skillData = GameData.getAvatarSkillDataMap().get(skillId);
if (skillData == null) {
return;
}
// Check if the skill is an elemental burst.
if (skillData.getCostElemVal() <= 0) {
return;
}
// Set the player as invulnerable.
this.abilityInvulnerable = true;
}
/**
* Invoked when a player ends a skill.
*
* @param player The player who started the skill.
*/
public void onSkillEnd(Player player) {
// Check if the player matches this player.
if (player.getUid() != this.player.getUid()) {
return;
}
// Check if the player is invulnerable.
if (!this.abilityInvulnerable) {
return;
}
// Set the player as not invulnerable.
this.abilityInvulnerable = false;
}
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = this.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 = this.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 = this.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.MODIFIER_ACTION_REMOVED) {
gatherObject.dropItems(this.getPlayer());
return;
}
}
// Sanity checks
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
GameEntity sourceEntity = this.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.MODIFIER_ACTION_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()) {
this.invokeAction(action, target, sourceEntity);
}
}
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.MODIFIER_ACTION_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()) {
this.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()));
this.getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke)
throws InvalidProtocolBufferException {
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
/**
* Handles a float value ability entry.
*
* @param invoke The ability invoke entry.
*/
private void handleGlobalFloatValue(AbilityInvokeEntry invoke)
throws InvalidProtocolBufferException {
var entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
if (entry.getKey().hasStr()
&& entry.hasFloatValue()
&& entry.getFloatValue() == 2.0f
&& entry.getKey().getStr().equals("_ABILITY_UziExplode_Count")) {
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, 10006);
}
}
private void invokeAction(
AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
switch (action.type) {
case HealHP -> {}
case LoseHP -> {
if (action.amountByTargetCurrentHPRatio == null) {
return;
}
float damageAmount = action.amount.get();
// if (action.amount.isDynamic && action.amount.dynamicKey != null) {
// damageAmount =
// sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
// }
if (damageAmount > 0) {
target.damage(damageAmount);
}
}
default -> {}
}
}
}
package emu.grasscutter.game.ability;
import com.google.protobuf.InvalidProtocolBufferException;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.AbilityModifierEntry;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.entity.gadget.GadgetGatherObject;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap;
import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina;
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
import lombok.Getter;
public final class AbilityManager extends BasePlayerManager {
HealAbilityManager healAbilityManager;
@Getter private boolean abilityInvulnerable = false;
public AbilityManager(Player player) {
super(player);
this.healAbilityManager = new HealAbilityManager(player);
}
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
this.healAbilityManager.healHandler(invoke);
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue()
// + "): " + Utils.bytesToHex(invoke.toByteArray()));
switch (invoke.getArgumentType()) {
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM -> this.handleOverrideParam(invoke);
case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP -> this.handleReinitOverrideMap(invoke);
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE -> this.handleModifierChange(invoke);
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA -> this.handleMixinCostStamina(invoke);
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL -> this.handleGenerateElemBall(invoke);
case ABILITY_INVOKE_ARGUMENT_META_GLOBAL_FLOAT_VALUE -> this.handleGlobalFloatValue(invoke);
default -> {}
}
}
/**
* Invoked when a player starts a skill.
*
* @param player The player who started the skill.
* @param skillId The skill ID.
* @param casterId The caster ID.
*/
public void onSkillStart(Player player, int skillId, int casterId) {
// Check if the player matches this player.
if (player.getUid() != this.player.getUid()) {
return;
}
// Check if the caster matches the player.
if (player.getTeamManager().getCurrentAvatarEntity().getId() != casterId) {
return;
}
var skillData = GameData.getAvatarSkillDataMap().get(skillId);
if (skillData == null) {
return;
}
// Check if the skill is an elemental burst.
if (skillData.getCostElemVal() <= 0) {
return;
}
// Set the player as invulnerable.
this.abilityInvulnerable = true;
}
/**
* Invoked when a player ends a skill.
*
* @param player The player who started the skill.
*/
public void onSkillEnd(Player player) {
// Check if the player matches this player.
if (player.getUid() != this.player.getUid()) {
return;
}
// Check if the player is invulnerable.
if (!this.abilityInvulnerable) {
return;
}
// Set the player as not invulnerable.
this.abilityInvulnerable = false;
}
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
GameEntity entity = this.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 = this.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 = this.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.MODIFIER_ACTION_REMOVED) {
gatherObject.dropItems(this.getPlayer());
return;
}
}
// Sanity checks
AbilityInvokeEntryHead head = invoke.getHead();
if (head == null) {
return;
}
GameEntity sourceEntity = this.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.MODIFIER_ACTION_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()) {
this.invokeAction(action, target, sourceEntity);
}
}
// Add to meta modifier list
target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString);
} else if (data.getAction() == ModifierAction.MODIFIER_ACTION_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()) {
this.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()));
this.getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim());
}
private void handleGenerateElemBall(AbilityInvokeEntry invoke)
throws InvalidProtocolBufferException {
this.player.getEnergyManager().handleGenerateElemBall(invoke);
}
/**
* Handles a float value ability entry.
*
* @param invoke The ability invoke entry.
*/
private void handleGlobalFloatValue(AbilityInvokeEntry invoke)
throws InvalidProtocolBufferException {
var entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
if (entry.getKey().hasStr()
&& entry.hasFloatValue()
&& entry.getFloatValue() == 2.0f
&& entry.getKey().getStr().equals("_ABILITY_UziExplode_Count")) {
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, 10006);
}
}
private void invokeAction(
AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
switch (action.type) {
case HealHP -> {}
case LoseHP -> {
if (action.amountByTargetCurrentHPRatio == null) {
return;
}
float damageAmount = action.amount.get();
// if (action.amount.isDynamic && action.amount.dynamicKey != null) {
// damageAmount =
// sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
// }
if (damageAmount > 0) {
target.damage(damageAmount);
}
}
default -> {}
}
}
}

View File

@@ -1,323 +1,323 @@
package emu.grasscutter.game.achievement;
import com.github.davidmoten.guavamini.Lists;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.achievement.AchievementData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.StatusOuterClass;
import emu.grasscutter.server.packet.send.PacketAchievementAllDataNotify;
import emu.grasscutter.server.packet.send.PacketAchievementUpdateNotify;
import emu.grasscutter.server.packet.send.PacketTakeAchievementGoalRewardRsp;
import emu.grasscutter.server.packet.send.PacketTakeAchievementRewardRsp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntSupplier;
import javax.annotation.Nullable;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.bson.types.ObjectId;
@Entity("achievements")
@Data
@Builder(builderMethodName = "of")
public class Achievements {
private static final IntSupplier currentTimeSecs =
() -> (int) (System.currentTimeMillis() / 1000L);
private static final Achievement INVALID =
new Achievement(StatusOuterClass.Status.STATUS_INVALID, -1, 0, 0, 0);
@Id private ObjectId id;
private int uid;
@Transient private Player player;
private Map<Integer, Achievement> achievementList;
@Getter private int finishedAchievementNum;
private List<Integer> takenGoalRewardIdList;
public static Achievements getByPlayer(Player player) {
var achievements =
player.getAchievements() == null
? DatabaseHelper.getAchievementData(player.getUid())
: player.getAchievements();
if (achievements == null) {
achievements = create(player.getUid());
}
return achievements;
}
public static Achievements create(int uid) {
var newAchievement =
Achievements.of()
.uid(uid)
.achievementList(init())
.finishedAchievementNum(0)
.takenGoalRewardIdList(Lists.newArrayList())
.build();
newAchievement.save();
return newAchievement;
}
private static Map<Integer, Achievement> init() {
Map<Integer, Achievement> map = new HashMap<>();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.forEach(
a -> {
map.put(
a.getId(),
new Achievement(
StatusOuterClass.Status.STATUS_UNFINISHED, a.getId(), a.getProgress(), 0, 0));
});
return map;
}
public AchievementControlReturns grant(int achievementId) {
var a = this.getAchievement(achievementId);
if (a == null || this.isFinished(achievementId)) {
return a == null
? AchievementControlReturns.achievementNotFound()
: AchievementControlReturns.alreadyAchieved();
}
return this.progress(achievementId, a.getTotalProgress());
}
public AchievementControlReturns revoke(int achievementId) {
var a = this.getAchievement(achievementId);
if (a == null || !this.isFinished(achievementId)) {
return a == null
? AchievementControlReturns.achievementNotFound()
: AchievementControlReturns.notYetAchieved();
}
return this.progress(achievementId, 0);
}
public AchievementControlReturns progress(int achievementId, int progress) {
var a = this.getAchievement(achievementId);
if (a == null) {
return AchievementControlReturns.achievementNotFound();
}
a.setCurProgress(progress);
return AchievementControlReturns.success(this.notifyOtherAchievements(a));
}
private int notifyOtherAchievements(Achievement a) {
var changedNum = new AtomicInteger();
changedNum.addAndGet(this.update(a) ? 1 : 0);
GameData.getAchievementDataMap().get(a.getId()).getExcludedGroupAchievementIdList().stream()
.map(this::getAchievement)
.filter(Objects::nonNull)
.forEach(
other -> {
other.setCurProgress(a.getCurProgress());
changedNum.addAndGet(this.update(other) ? 1 : 0);
});
this.computeFinishedAchievementNum();
this.save();
this.sendUpdatePacket(a);
return changedNum.intValue();
}
private boolean update(Achievement a) {
if (a.getStatus() == StatusOuterClass.Status.STATUS_UNFINISHED
&& a.getCurProgress() >= a.getTotalProgress()) {
a.setStatus(StatusOuterClass.Status.STATUS_FINISHED);
a.setFinishTimestampSec(currentTimeSecs.getAsInt());
return true;
} else if (this.isFinished(a.getId()) && a.getCurProgress() < a.getTotalProgress()) {
a.setStatus(StatusOuterClass.Status.STATUS_UNFINISHED);
a.setFinishTimestampSec(0);
return true;
}
return false;
}
private void computeFinishedAchievementNum() {
this.finishedAchievementNum =
GameData.getAchievementDataMap().values().stream()
.filter(a -> this.isFinished(a.getId()))
.mapToInt(value -> 1)
.sum();
}
private void sendUpdatePacket(Achievement achievement) {
List<Achievement> achievements = Lists.newArrayList(achievement);
achievements.addAll(
GameData.getAchievementDataMap()
.get(achievement.getId())
.getExcludedGroupAchievementIdList()
.stream()
.map(this::getAchievement)
.filter(Objects::nonNull)
.toList());
this.sendUpdatePacket(achievements);
}
private void sendUpdatePacket(List<Achievement> achievement) {
if (this.isPacketSendable()) {
this.player.sendPacket(new PacketAchievementUpdateNotify(achievement));
}
}
@Nullable public Achievement getAchievement(int achievementId) {
if (this.isInvalid(achievementId)) {
return null;
}
return this.getAchievementList()
.computeIfAbsent(
achievementId,
id -> {
return new Achievement(
StatusOuterClass.Status.STATUS_UNFINISHED,
id,
GameData.getAchievementDataMap().get(id.intValue()).getProgress(),
0,
0);
});
}
public boolean isInvalid(int achievementId) {
var data = GameData.getAchievementDataMap().get(achievementId);
return data == null || data.isDisuse();
}
public StatusOuterClass.Status getStatus(int achievementId) {
return this.getAchievementList().getOrDefault(achievementId, INVALID).getStatus();
}
public boolean isFinished(int achievementId) {
var status = this.getStatus(achievementId);
return status == StatusOuterClass.Status.STATUS_FINISHED
|| status == StatusOuterClass.Status.STATUS_REWARD_TAKEN;
}
public void takeReward(List<Integer> ids) {
List<GameItem> rewards = Lists.newArrayList();
for (int i : ids) {
var target = GameData.getAchievementDataMap().get(i);
if (target == null) {
Grasscutter.getLogger().warn("null returned while taking reward!");
return;
}
if (this.isRewardTaken(i)) {
this.player.sendPacket(new PacketTakeAchievementRewardRsp());
return;
}
var data = GameData.getRewardDataMap().get(target.getFinishRewardId());
if (data == null) {
Grasscutter.getLogger().warn("null returned while getting reward data!");
continue;
}
data.getRewardItemList()
.forEach(
itemParamData -> {
var itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
Grasscutter.getLogger().warn("itemData == null!");
return;
}
rewards.add(new GameItem(itemData, itemParamData.getCount()));
});
var a = this.getAchievement(i);
a.setStatus(StatusOuterClass.Status.STATUS_REWARD_TAKEN);
this.save();
this.sendUpdatePacket(a);
}
this.player.getInventory().addItems(rewards, ActionReason.AchievementReward);
this.player.sendPacket(
new PacketTakeAchievementRewardRsp(
ids, rewards.stream().map(GameItem::toItemParam).toList()));
}
public void takeGoalReward(List<Integer> ids) {
List<GameItem> rewards = Lists.newArrayList();
for (int i : ids) {
if (this.takenGoalRewardIdList.contains(i)) {
this.player.sendPacket(new PacketTakeAchievementGoalRewardRsp());
}
var goalData = GameData.getAchievementGoalDataMap().get(i);
if (goalData == null) {
Grasscutter.getLogger().warn("null returned while getting goal reward data!");
continue;
}
var data = GameData.getRewardDataMap().get(goalData.getFinishRewardId());
if (data == null) {
Grasscutter.getLogger().warn("null returned while getting reward data!");
continue;
}
data.getRewardItemList()
.forEach(
itemParamData -> {
var itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
Grasscutter.getLogger().warn("itemData == null!");
return;
}
rewards.add(new GameItem(itemData, itemParamData.getCount()));
});
this.takenGoalRewardIdList.add(i);
this.save();
}
this.player.getInventory().addItems(rewards, ActionReason.AchievementGoalReward);
this.player.sendPacket(
new PacketTakeAchievementGoalRewardRsp(
ids, rewards.stream().map(GameItem::toItemParam).toList()));
}
public boolean isRewardTaken(int achievementId) {
return this.getStatus(achievementId) == StatusOuterClass.Status.STATUS_REWARD_TAKEN;
}
public boolean isRewardLeft(int achievementId) {
return this.getStatus(achievementId) == StatusOuterClass.Status.STATUS_FINISHED;
}
private boolean isPacketSendable() {
return this.player != null;
}
public void save() {
DatabaseHelper.saveAchievementData(this);
}
public void onLogin(Player player) {
if (this.player == null) {
this.player = player;
}
this.player.sendPacket(new PacketAchievementAllDataNotify(this.player));
}
}
package emu.grasscutter.game.achievement;
import com.github.davidmoten.guavamini.Lists;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.achievement.AchievementData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.StatusOuterClass;
import emu.grasscutter.server.packet.send.PacketAchievementAllDataNotify;
import emu.grasscutter.server.packet.send.PacketAchievementUpdateNotify;
import emu.grasscutter.server.packet.send.PacketTakeAchievementGoalRewardRsp;
import emu.grasscutter.server.packet.send.PacketTakeAchievementRewardRsp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.IntSupplier;
import javax.annotation.Nullable;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import org.bson.types.ObjectId;
@Entity("achievements")
@Data
@Builder(builderMethodName = "of")
public class Achievements {
private static final IntSupplier currentTimeSecs =
() -> (int) (System.currentTimeMillis() / 1000L);
private static final Achievement INVALID =
new Achievement(StatusOuterClass.Status.STATUS_INVALID, -1, 0, 0, 0);
@Id private ObjectId id;
private int uid;
@Transient private Player player;
private Map<Integer, Achievement> achievementList;
@Getter private int finishedAchievementNum;
private List<Integer> takenGoalRewardIdList;
public static Achievements getByPlayer(Player player) {
var achievements =
player.getAchievements() == null
? DatabaseHelper.getAchievementData(player.getUid())
: player.getAchievements();
if (achievements == null) {
achievements = create(player.getUid());
}
return achievements;
}
public static Achievements create(int uid) {
var newAchievement =
Achievements.of()
.uid(uid)
.achievementList(init())
.finishedAchievementNum(0)
.takenGoalRewardIdList(Lists.newArrayList())
.build();
newAchievement.save();
return newAchievement;
}
private static Map<Integer, Achievement> init() {
Map<Integer, Achievement> map = new HashMap<>();
GameData.getAchievementDataMap().values().stream()
.filter(AchievementData::isUsed)
.forEach(
a -> {
map.put(
a.getId(),
new Achievement(
StatusOuterClass.Status.STATUS_UNFINISHED, a.getId(), a.getProgress(), 0, 0));
});
return map;
}
public AchievementControlReturns grant(int achievementId) {
var a = this.getAchievement(achievementId);
if (a == null || this.isFinished(achievementId)) {
return a == null
? AchievementControlReturns.achievementNotFound()
: AchievementControlReturns.alreadyAchieved();
}
return this.progress(achievementId, a.getTotalProgress());
}
public AchievementControlReturns revoke(int achievementId) {
var a = this.getAchievement(achievementId);
if (a == null || !this.isFinished(achievementId)) {
return a == null
? AchievementControlReturns.achievementNotFound()
: AchievementControlReturns.notYetAchieved();
}
return this.progress(achievementId, 0);
}
public AchievementControlReturns progress(int achievementId, int progress) {
var a = this.getAchievement(achievementId);
if (a == null) {
return AchievementControlReturns.achievementNotFound();
}
a.setCurProgress(progress);
return AchievementControlReturns.success(this.notifyOtherAchievements(a));
}
private int notifyOtherAchievements(Achievement a) {
var changedNum = new AtomicInteger();
changedNum.addAndGet(this.update(a) ? 1 : 0);
GameData.getAchievementDataMap().get(a.getId()).getExcludedGroupAchievementIdList().stream()
.map(this::getAchievement)
.filter(Objects::nonNull)
.forEach(
other -> {
other.setCurProgress(a.getCurProgress());
changedNum.addAndGet(this.update(other) ? 1 : 0);
});
this.computeFinishedAchievementNum();
this.save();
this.sendUpdatePacket(a);
return changedNum.intValue();
}
private boolean update(Achievement a) {
if (a.getStatus() == StatusOuterClass.Status.STATUS_UNFINISHED
&& a.getCurProgress() >= a.getTotalProgress()) {
a.setStatus(StatusOuterClass.Status.STATUS_FINISHED);
a.setFinishTimestampSec(currentTimeSecs.getAsInt());
return true;
} else if (this.isFinished(a.getId()) && a.getCurProgress() < a.getTotalProgress()) {
a.setStatus(StatusOuterClass.Status.STATUS_UNFINISHED);
a.setFinishTimestampSec(0);
return true;
}
return false;
}
private void computeFinishedAchievementNum() {
this.finishedAchievementNum =
GameData.getAchievementDataMap().values().stream()
.filter(a -> this.isFinished(a.getId()))
.mapToInt(value -> 1)
.sum();
}
private void sendUpdatePacket(Achievement achievement) {
List<Achievement> achievements = Lists.newArrayList(achievement);
achievements.addAll(
GameData.getAchievementDataMap()
.get(achievement.getId())
.getExcludedGroupAchievementIdList()
.stream()
.map(this::getAchievement)
.filter(Objects::nonNull)
.toList());
this.sendUpdatePacket(achievements);
}
private void sendUpdatePacket(List<Achievement> achievement) {
if (this.isPacketSendable()) {
this.player.sendPacket(new PacketAchievementUpdateNotify(achievement));
}
}
@Nullable public Achievement getAchievement(int achievementId) {
if (this.isInvalid(achievementId)) {
return null;
}
return this.getAchievementList()
.computeIfAbsent(
achievementId,
id -> {
return new Achievement(
StatusOuterClass.Status.STATUS_UNFINISHED,
id,
GameData.getAchievementDataMap().get(id.intValue()).getProgress(),
0,
0);
});
}
public boolean isInvalid(int achievementId) {
var data = GameData.getAchievementDataMap().get(achievementId);
return data == null || data.isDisuse();
}
public StatusOuterClass.Status getStatus(int achievementId) {
return this.getAchievementList().getOrDefault(achievementId, INVALID).getStatus();
}
public boolean isFinished(int achievementId) {
var status = this.getStatus(achievementId);
return status == StatusOuterClass.Status.STATUS_FINISHED
|| status == StatusOuterClass.Status.STATUS_REWARD_TAKEN;
}
public void takeReward(List<Integer> ids) {
List<GameItem> rewards = Lists.newArrayList();
for (int i : ids) {
var target = GameData.getAchievementDataMap().get(i);
if (target == null) {
Grasscutter.getLogger().warn("null returned while taking reward!");
return;
}
if (this.isRewardTaken(i)) {
this.player.sendPacket(new PacketTakeAchievementRewardRsp());
return;
}
var data = GameData.getRewardDataMap().get(target.getFinishRewardId());
if (data == null) {
Grasscutter.getLogger().warn("null returned while getting reward data!");
continue;
}
data.getRewardItemList()
.forEach(
itemParamData -> {
var itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
Grasscutter.getLogger().warn("itemData == null!");
return;
}
rewards.add(new GameItem(itemData, itemParamData.getCount()));
});
var a = this.getAchievement(i);
a.setStatus(StatusOuterClass.Status.STATUS_REWARD_TAKEN);
this.save();
this.sendUpdatePacket(a);
}
this.player.getInventory().addItems(rewards, ActionReason.AchievementReward);
this.player.sendPacket(
new PacketTakeAchievementRewardRsp(
ids, rewards.stream().map(GameItem::toItemParam).toList()));
}
public void takeGoalReward(List<Integer> ids) {
List<GameItem> rewards = Lists.newArrayList();
for (int i : ids) {
if (this.takenGoalRewardIdList.contains(i)) {
this.player.sendPacket(new PacketTakeAchievementGoalRewardRsp());
}
var goalData = GameData.getAchievementGoalDataMap().get(i);
if (goalData == null) {
Grasscutter.getLogger().warn("null returned while getting goal reward data!");
continue;
}
var data = GameData.getRewardDataMap().get(goalData.getFinishRewardId());
if (data == null) {
Grasscutter.getLogger().warn("null returned while getting reward data!");
continue;
}
data.getRewardItemList()
.forEach(
itemParamData -> {
var itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
Grasscutter.getLogger().warn("itemData == null!");
return;
}
rewards.add(new GameItem(itemData, itemParamData.getCount()));
});
this.takenGoalRewardIdList.add(i);
this.save();
}
this.player.getInventory().addItems(rewards, ActionReason.AchievementGoalReward);
this.player.sendPacket(
new PacketTakeAchievementGoalRewardRsp(
ids, rewards.stream().map(GameItem::toItemParam).toList()));
}
public boolean isRewardTaken(int achievementId) {
return this.getStatus(achievementId) == StatusOuterClass.Status.STATUS_REWARD_TAKEN;
}
public boolean isRewardLeft(int achievementId) {
return this.getStatus(achievementId) == StatusOuterClass.Status.STATUS_FINISHED;
}
private boolean isPacketSendable() {
return this.player != null;
}
public void save() {
DatabaseHelper.saveAchievementData(this);
}
public void onLogin(Player player) {
if (this.player == null) {
this.player = player;
}
this.player.sendPacket(new PacketAchievementAllDataNotify(this.player));
}
}

View File

@@ -1,32 +1,32 @@
package emu.grasscutter.game.activity;
import java.util.Date;
import java.util.List;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityConfigItem {
int activityId;
int activityType;
int scheduleId;
List<Integer> meetCondList;
Date beginTime;
Date openTime;
Date closeTime;
Date endTime;
transient ActivityHandler activityHandler;
void onLoad() {
if (openTime == null) {
this.openTime = beginTime;
}
if (closeTime == null) {
this.closeTime = endTime;
}
}
}
package emu.grasscutter.game.activity;
import java.util.Date;
import java.util.List;
import lombok.AccessLevel;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityConfigItem {
int activityId;
int activityType;
int scheduleId;
List<Integer> meetCondList;
Date beginTime;
Date openTime;
Date closeTime;
Date endTime;
transient ActivityHandler activityHandler;
void onLoad() {
if (openTime == null) {
this.openTime = beginTime;
}
if (closeTime == null) {
this.closeTime = endTime;
}
}
}

View File

@@ -1,139 +1,139 @@
package emu.grasscutter.game.activity;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.activity.ActivityData;
import emu.grasscutter.data.server.ActivityCondGroup;
import emu.grasscutter.game.activity.condition.ActivityConditionExecutor;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.utils.DateHelper;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public abstract class ActivityHandler {
/** Must set before initWatchers */
@Getter ActivityConfigItem activityConfigItem;
@Getter ActivityData activityData;
Map<WatcherTriggerType, List<ActivityWatcher>> watchersMap = new HashMap<>();
public abstract void onProtoBuild(
PlayerActivityData playerActivityData,
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo);
public abstract void onInitPlayerActivityData(PlayerActivityData playerActivityData);
public void initWatchers(Map<WatcherTriggerType, ConstructorAccess<?>> activityWatcherTypeMap) {
activityData = GameData.getActivityDataMap().get(activityConfigItem.getActivityId());
// add watcher to map by id
activityData
.getWatcherDataList()
.forEach(
watcherData -> {
var watcherType =
activityWatcherTypeMap.get(
watcherData.getTriggerConfig().getWatcherTriggerType());
ActivityWatcher watcher;
if (watcherType != null) {
watcher = (ActivityWatcher) watcherType.newInstance();
} else {
watcher = new DefaultWatcher();
}
watcher.setWatcherId(watcherData.getId());
watcher.setActivityHandler(this);
watcher.setActivityWatcherData(watcherData);
watchersMap.computeIfAbsent(
watcherData.getTriggerConfig().getWatcherTriggerType(), k -> new ArrayList<>());
watchersMap.get(watcherData.getTriggerConfig().getWatcherTriggerType()).add(watcher);
});
}
protected void triggerCondEvents(Player player) {
if (activityData == null) {
return;
}
var questManager = player.getQuestManager();
activityData
.getCondGroupId()
.forEach(
condGroupId -> {
var condGroup = GameData.getActivityCondGroupMap().get((int) condGroupId);
condGroup
.getCondIds()
.forEach(
condition ->
questManager.queueEvent(QuestCond.QUEST_COND_ACTIVITY_COND, condition));
});
}
private List<Integer> getActivityConditions() {
if (activityData == null) {
return new ArrayList<>();
}
return activityData.getCondGroupId().stream()
.map(condGroupId -> GameData.getActivityCondGroupMap().get((int) condGroupId))
.filter(Objects::nonNull)
.map(ActivityCondGroup::getCondIds)
.flatMap(Collection::stream)
.toList();
}
// TODO handle possible overwrites
private List<Integer> getMeetConditions(ActivityConditionExecutor conditionExecutor) {
return conditionExecutor.getMeetActivitiesConditions(getActivityConditions());
}
private Map<Integer, PlayerActivityData.WatcherInfo> initWatchersDataForPlayer() {
return watchersMap.values().stream()
.flatMap(Collection::stream)
.map(PlayerActivityData.WatcherInfo::init)
.collect(Collectors.toMap(PlayerActivityData.WatcherInfo::getWatcherId, y -> y));
}
public PlayerActivityData initPlayerActivityData(Player player) {
PlayerActivityData playerActivityData =
PlayerActivityData.of()
.activityId(activityConfigItem.getActivityId())
.uid(player.getUid())
.watcherInfoMap(initWatchersDataForPlayer())
.build();
onInitPlayerActivityData(playerActivityData);
return playerActivityData;
}
public ActivityInfoOuterClass.ActivityInfo toProto(
PlayerActivityData playerActivityData, ActivityConditionExecutor conditionExecutor) {
var proto = ActivityInfoOuterClass.ActivityInfo.newBuilder();
proto
.setActivityId(activityConfigItem.getActivityId())
.setActivityType(activityConfigItem.getActivityType())
.setScheduleId(activityConfigItem.getScheduleId())
.setBeginTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
.setFirstDayStartTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
.setEndTime(DateHelper.getUnixTime(activityConfigItem.getEndTime()))
.addAllMeetCondList(getMeetConditions(conditionExecutor));
if (playerActivityData != null) {
proto.addAllWatcherInfoList(playerActivityData.getAllWatcherInfoList());
}
onProtoBuild(playerActivityData, proto);
return proto.build();
}
}
package emu.grasscutter.game.activity;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.activity.ActivityData;
import emu.grasscutter.data.server.ActivityCondGroup;
import emu.grasscutter.game.activity.condition.ActivityConditionExecutor;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.utils.DateHelper;
import java.util.*;
import java.util.stream.Collectors;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public abstract class ActivityHandler {
/** Must set before initWatchers */
@Getter ActivityConfigItem activityConfigItem;
@Getter ActivityData activityData;
Map<WatcherTriggerType, List<ActivityWatcher>> watchersMap = new HashMap<>();
public abstract void onProtoBuild(
PlayerActivityData playerActivityData,
ActivityInfoOuterClass.ActivityInfo.Builder activityInfo);
public abstract void onInitPlayerActivityData(PlayerActivityData playerActivityData);
public void initWatchers(Map<WatcherTriggerType, ConstructorAccess<?>> activityWatcherTypeMap) {
activityData = GameData.getActivityDataMap().get(activityConfigItem.getActivityId());
// add watcher to map by id
activityData
.getWatcherDataList()
.forEach(
watcherData -> {
var watcherType =
activityWatcherTypeMap.get(
watcherData.getTriggerConfig().getWatcherTriggerType());
ActivityWatcher watcher;
if (watcherType != null) {
watcher = (ActivityWatcher) watcherType.newInstance();
} else {
watcher = new DefaultWatcher();
}
watcher.setWatcherId(watcherData.getId());
watcher.setActivityHandler(this);
watcher.setActivityWatcherData(watcherData);
watchersMap.computeIfAbsent(
watcherData.getTriggerConfig().getWatcherTriggerType(), k -> new ArrayList<>());
watchersMap.get(watcherData.getTriggerConfig().getWatcherTriggerType()).add(watcher);
});
}
protected void triggerCondEvents(Player player) {
if (activityData == null) {
return;
}
var questManager = player.getQuestManager();
activityData
.getCondGroupId()
.forEach(
condGroupId -> {
var condGroup = GameData.getActivityCondGroupMap().get((int) condGroupId);
condGroup
.getCondIds()
.forEach(
condition ->
questManager.queueEvent(QuestCond.QUEST_COND_ACTIVITY_COND, condition));
});
}
private List<Integer> getActivityConditions() {
if (activityData == null) {
return new ArrayList<>();
}
return activityData.getCondGroupId().stream()
.map(condGroupId -> GameData.getActivityCondGroupMap().get((int) condGroupId))
.filter(Objects::nonNull)
.map(ActivityCondGroup::getCondIds)
.flatMap(Collection::stream)
.toList();
}
// TODO handle possible overwrites
private List<Integer> getMeetConditions(ActivityConditionExecutor conditionExecutor) {
return conditionExecutor.getMeetActivitiesConditions(getActivityConditions());
}
private Map<Integer, PlayerActivityData.WatcherInfo> initWatchersDataForPlayer() {
return watchersMap.values().stream()
.flatMap(Collection::stream)
.map(PlayerActivityData.WatcherInfo::init)
.collect(Collectors.toMap(PlayerActivityData.WatcherInfo::getWatcherId, y -> y));
}
public PlayerActivityData initPlayerActivityData(Player player) {
PlayerActivityData playerActivityData =
PlayerActivityData.of()
.activityId(activityConfigItem.getActivityId())
.uid(player.getUid())
.watcherInfoMap(initWatchersDataForPlayer())
.build();
onInitPlayerActivityData(playerActivityData);
return playerActivityData;
}
public ActivityInfoOuterClass.ActivityInfo toProto(
PlayerActivityData playerActivityData, ActivityConditionExecutor conditionExecutor) {
var proto = ActivityInfoOuterClass.ActivityInfo.newBuilder();
proto
.setActivityId(activityConfigItem.getActivityId())
.setActivityType(activityConfigItem.getActivityType())
.setScheduleId(activityConfigItem.getScheduleId())
.setBeginTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
.setFirstDayStartTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
.setEndTime(DateHelper.getUnixTime(activityConfigItem.getEndTime()))
.addAllMeetCondList(getMeetConditions(conditionExecutor));
if (playerActivityData != null) {
proto.addAllWatcherInfoList(playerActivityData.getAllWatcherInfoList());
}
onProtoBuild(playerActivityData, proto);
return proto.build();
}
}

View File

@@ -1,231 +1,231 @@
package emu.grasscutter.game.activity;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.activity.condition.*;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import org.reflections.Reflections;
@Getter
public class ActivityManager extends BasePlayerManager {
private static final Map<Integer, ActivityConfigItem> activityConfigItemMap;
@Getter private static final Map<Integer, ActivityConfigItem> scheduleActivityConfigMap;
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
private final ActivityConditionExecutor conditionExecutor;
static {
activityConfigItemMap = new HashMap<>();
scheduleActivityConfigMap = new HashMap<>();
loadActivityConfigData();
}
private static void loadActivityConfigData() {
// scan activity type handler & watcher type
var activityHandlerTypeMap = new HashMap<ActivityType, ConstructorAccess<?>>();
var activityWatcherTypeMap = new HashMap<WatcherTriggerType, ConstructorAccess<?>>();
var reflections = new Reflections(ActivityManager.class.getPackage().getName());
reflections
.getSubTypesOf(ActivityHandler.class)
.forEach(
item -> {
var typeName = item.getAnnotation(GameActivity.class);
activityHandlerTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
reflections
.getSubTypesOf(ActivityWatcher.class)
.forEach(
item -> {
var typeName = item.getAnnotation(ActivityWatcherType.class);
activityWatcherTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
try {
DataLoader.loadList("ActivityConfig.json", ActivityConfigItem.class)
.forEach(
item -> {
item.onLoad();
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
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) {
activityHandler = (ActivityHandler) activityHandlerType.newInstance();
} else {
activityHandler = new DefaultActivityHandler();
}
activityHandler.setActivityConfigItem(item);
activityHandler.initWatchers(activityWatcherTypeMap);
item.setActivityHandler(activityHandler);
activityConfigItemMap.putIfAbsent(item.getActivityId(), item);
scheduleActivityConfigMap.putIfAbsent(item.getScheduleId(), item);
});
Grasscutter.getLogger().info("Enable {} activities.", activityConfigItemMap.size());
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load activities config.", e);
}
}
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) {
data = item.getActivityHandler().initPlayerActivityData(player);
data.save();
}
data.setPlayer(player);
data.setActivityHandler(item.getActivityHandler());
playerActivityDataMap.put(item.getActivityId(), data);
});
player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values()));
conditionExecutor =
new BasicActivityConditionExecutor(
activityConfigItemMap,
GameData.getActivityCondExcelConfigDataMap(),
PlayerActivityDataMappingBuilder.buildPlayerActivityDataByActivityCondId(
playerActivityDataMap),
AllActivityConditionBuilder.buildActivityConditions());
}
/** trigger activity watcher */
public void triggerWatcher(WatcherTriggerType watcherTriggerType, String... params) {
var watchers =
activityConfigItemMap.values().stream()
.map(ActivityConfigItem::getActivityHandler)
.filter(Objects::nonNull)
.map(ActivityHandler::getWatchersMap)
.map(map -> map.get(watcherTriggerType))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.toList();
watchers.forEach(
watcher ->
watcher.trigger(
playerActivityDataMap.get(
watcher.getActivityHandler().getActivityConfigItem().getActivityId()),
params));
}
public boolean isActivityActive(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getBeginTime()) && now.before(activityConfig.getEndTime());
}
public boolean hasActivityEnded(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return true;
}
return new Date().after(activityConfig.getEndTime());
}
public boolean isActivityOpen(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getOpenTime()) && now.before(activityConfig.getCloseTime());
}
public int getOpenDay(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return 0;
}
var now = new Date();
return (int)
TimeUnit.DAYS.convert(
now.getTime() - activityConfig.getOpenTime().getTime(), TimeUnit.MILLISECONDS)
+ 1;
}
public boolean isActivityClosed(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getCloseTime());
}
public boolean meetsCondition(int activityCondId) {
return conditionExecutor.meetsCondition(activityCondId);
}
public void triggerActivityConditions() {
activityConfigItemMap.forEach((k, v) -> v.getActivityHandler().triggerCondEvents(player));
}
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) {
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
var activityData = playerActivityDataMap.get(activityId);
return activityHandler.toProto(activityData, conditionExecutor);
}
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) {
return getActivityHandler(type).map(x -> (T) x);
}
public Optional<Integer> getActivityIdByActivityType(ActivityType type) {
return getActivityHandler(type)
.map(ActivityHandler::getActivityConfigItem)
.map(ActivityConfigItem::getActivityId);
}
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type) {
return getActivityIdByActivityType(type).map(playerActivityDataMap::get);
}
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(
ActivityType type) {
return getActivityIdByActivityType(type).map(this::getInfoProtoByActivityId);
}
}
package emu.grasscutter.game.activity;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.activity.condition.*;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import org.reflections.Reflections;
@Getter
public class ActivityManager extends BasePlayerManager {
private static final Map<Integer, ActivityConfigItem> activityConfigItemMap;
@Getter private static final Map<Integer, ActivityConfigItem> scheduleActivityConfigMap;
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
private final ActivityConditionExecutor conditionExecutor;
static {
activityConfigItemMap = new HashMap<>();
scheduleActivityConfigMap = new HashMap<>();
loadActivityConfigData();
}
private static void loadActivityConfigData() {
// scan activity type handler & watcher type
var activityHandlerTypeMap = new HashMap<ActivityType, ConstructorAccess<?>>();
var activityWatcherTypeMap = new HashMap<WatcherTriggerType, ConstructorAccess<?>>();
var reflections = new Reflections(ActivityManager.class.getPackage().getName());
reflections
.getSubTypesOf(ActivityHandler.class)
.forEach(
item -> {
var typeName = item.getAnnotation(GameActivity.class);
activityHandlerTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
reflections
.getSubTypesOf(ActivityWatcher.class)
.forEach(
item -> {
var typeName = item.getAnnotation(ActivityWatcherType.class);
activityWatcherTypeMap.put(typeName.value(), ConstructorAccess.get(item));
});
try {
DataLoader.loadList("ActivityConfig.json", ActivityConfigItem.class)
.forEach(
item -> {
item.onLoad();
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
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) {
activityHandler = (ActivityHandler) activityHandlerType.newInstance();
} else {
activityHandler = new DefaultActivityHandler();
}
activityHandler.setActivityConfigItem(item);
activityHandler.initWatchers(activityWatcherTypeMap);
item.setActivityHandler(activityHandler);
activityConfigItemMap.putIfAbsent(item.getActivityId(), item);
scheduleActivityConfigMap.putIfAbsent(item.getScheduleId(), item);
});
Grasscutter.getLogger().info("Enable {} activities.", activityConfigItemMap.size());
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load activities config.", e);
}
}
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) {
data = item.getActivityHandler().initPlayerActivityData(player);
data.save();
}
data.setPlayer(player);
data.setActivityHandler(item.getActivityHandler());
playerActivityDataMap.put(item.getActivityId(), data);
});
player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values()));
conditionExecutor =
new BasicActivityConditionExecutor(
activityConfigItemMap,
GameData.getActivityCondExcelConfigDataMap(),
PlayerActivityDataMappingBuilder.buildPlayerActivityDataByActivityCondId(
playerActivityDataMap),
AllActivityConditionBuilder.buildActivityConditions());
}
/** trigger activity watcher */
public void triggerWatcher(WatcherTriggerType watcherTriggerType, String... params) {
var watchers =
activityConfigItemMap.values().stream()
.map(ActivityConfigItem::getActivityHandler)
.filter(Objects::nonNull)
.map(ActivityHandler::getWatchersMap)
.map(map -> map.get(watcherTriggerType))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.toList();
watchers.forEach(
watcher ->
watcher.trigger(
playerActivityDataMap.get(
watcher.getActivityHandler().getActivityConfigItem().getActivityId()),
params));
}
public boolean isActivityActive(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getBeginTime()) && now.before(activityConfig.getEndTime());
}
public boolean hasActivityEnded(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return true;
}
return new Date().after(activityConfig.getEndTime());
}
public boolean isActivityOpen(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getOpenTime()) && now.before(activityConfig.getCloseTime());
}
public int getOpenDay(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return 0;
}
var now = new Date();
return (int)
TimeUnit.DAYS.convert(
now.getTime() - activityConfig.getOpenTime().getTime(), TimeUnit.MILLISECONDS)
+ 1;
}
public boolean isActivityClosed(int activityId) {
var activityConfig = activityConfigItemMap.get(activityId);
if (activityConfig == null) {
return false;
}
var now = new Date();
return now.after(activityConfig.getCloseTime());
}
public boolean meetsCondition(int activityCondId) {
return conditionExecutor.meetsCondition(activityCondId);
}
public void triggerActivityConditions() {
activityConfigItemMap.forEach((k, v) -> v.getActivityHandler().triggerCondEvents(player));
}
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) {
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
var activityData = playerActivityDataMap.get(activityId);
return activityHandler.toProto(activityData, conditionExecutor);
}
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) {
return getActivityHandler(type).map(x -> (T) x);
}
public Optional<Integer> getActivityIdByActivityType(ActivityType type) {
return getActivityHandler(type)
.map(ActivityHandler::getActivityConfigItem)
.map(ActivityConfigItem::getActivityId);
}
public Optional<PlayerActivityData> getPlayerActivityDataByActivityType(ActivityType type) {
return getActivityIdByActivityType(type).map(playerActivityDataMap::get);
}
public Optional<ActivityInfoOuterClass.ActivityInfo> getInfoProtoByActivityType(
ActivityType type) {
return getActivityIdByActivityType(type).map(this::getInfoProtoByActivityId);
}
}

View File

@@ -1,25 +1,25 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public abstract class ActivityWatcher {
int watcherId;
ActivityWatcherData activityWatcherData;
ActivityHandler activityHandler;
protected abstract boolean isMeet(String... param);
public void trigger(PlayerActivityData playerActivityData, String... param) {
if (isMeet(param)) {
playerActivityData.addWatcherProgress(watcherId);
playerActivityData.save();
}
}
}
package emu.grasscutter.game.activity;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public abstract class ActivityWatcher {
int watcherId;
ActivityWatcherData activityWatcherData;
ActivityHandler activityHandler;
protected abstract boolean isMeet(String... param);
public void trigger(PlayerActivityData playerActivityData, String... param) {
if (isMeet(param)) {
playerActivityData.addWatcherProgress(watcherId);
playerActivityData.save();
}
}
}

View File

@@ -1,134 +1,134 @@
package emu.grasscutter.game.activity;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.ActivityWatcherInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify;
import emu.grasscutter.utils.JsonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity("activities")
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class PlayerActivityData {
@Id String id;
int uid;
int activityId;
Map<Integer, WatcherInfo> watcherInfoMap;
/** the detail data of each type of activity (Json format) */
String detail;
@Transient Player player;
@Transient ActivityHandler activityHandler;
public static PlayerActivityData getByPlayer(Player player, int activityId) {
return DatabaseHelper.getPlayerActivityData(player.getUid(), activityId);
}
public void save() {
DatabaseHelper.savePlayerActivityData(this);
}
public synchronized void addWatcherProgress(int watcherId) {
var watcherInfo = watcherInfoMap.get(watcherId);
if (watcherInfo == null) {
return;
}
if (watcherInfo.curProgress >= watcherInfo.totalProgress) {
return;
}
watcherInfo.curProgress++;
getPlayer().sendPacket(new PacketActivityUpdateWatcherNotify(activityId, watcherInfo));
}
public List<ActivityWatcherInfoOuterClass.ActivityWatcherInfo> getAllWatcherInfoList() {
return watcherInfoMap.values().stream().map(WatcherInfo::toProto).toList();
}
public void setDetail(Object detail) {
this.detail = JsonUtils.encode(detail);
}
public void takeWatcherReward(int watcherId) {
var watcher = watcherInfoMap.get(watcherId);
if (watcher == null || watcher.isTakenReward()) {
return;
}
var reward =
Optional.of(watcher)
.map(WatcherInfo::getMetadata)
.map(ActivityWatcherData::getRewardID)
.map(id -> GameData.getRewardDataMap().get(id.intValue()));
if (reward.isEmpty()) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.get().getRewardItemList()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.ActivityWatcher);
watcher.setTakenReward(true);
save();
}
@Entity
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public static class WatcherInfo {
int watcherId;
int totalProgress;
int curProgress;
boolean isTakenReward;
/**
* @return True when the progress of this watcher has reached the total progress.
*/
public boolean isFinished() {
return this.curProgress >= this.totalProgress;
}
public static WatcherInfo init(ActivityWatcher watcher) {
return WatcherInfo.of()
.watcherId(watcher.getWatcherId())
.totalProgress(watcher.getActivityWatcherData().getProgress())
.isTakenReward(false)
.build();
}
public ActivityWatcherData getMetadata() {
return GameData.getActivityWatcherDataMap().get(watcherId);
}
public ActivityWatcherInfoOuterClass.ActivityWatcherInfo toProto() {
return ActivityWatcherInfoOuterClass.ActivityWatcherInfo.newBuilder()
.setWatcherId(watcherId)
.setCurProgress(curProgress)
.setTotalProgress(totalProgress)
.setIsTakenReward(isTakenReward)
.build();
}
}
}
package emu.grasscutter.game.activity;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.ActivityWatcherInfoOuterClass;
import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify;
import emu.grasscutter.utils.JsonUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.FieldDefaults;
@Entity("activities")
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public class PlayerActivityData {
@Id String id;
int uid;
int activityId;
Map<Integer, WatcherInfo> watcherInfoMap;
/** the detail data of each type of activity (Json format) */
String detail;
@Transient Player player;
@Transient ActivityHandler activityHandler;
public static PlayerActivityData getByPlayer(Player player, int activityId) {
return DatabaseHelper.getPlayerActivityData(player.getUid(), activityId);
}
public void save() {
DatabaseHelper.savePlayerActivityData(this);
}
public synchronized void addWatcherProgress(int watcherId) {
var watcherInfo = watcherInfoMap.get(watcherId);
if (watcherInfo == null) {
return;
}
if (watcherInfo.curProgress >= watcherInfo.totalProgress) {
return;
}
watcherInfo.curProgress++;
getPlayer().sendPacket(new PacketActivityUpdateWatcherNotify(activityId, watcherInfo));
}
public List<ActivityWatcherInfoOuterClass.ActivityWatcherInfo> getAllWatcherInfoList() {
return watcherInfoMap.values().stream().map(WatcherInfo::toProto).toList();
}
public void setDetail(Object detail) {
this.detail = JsonUtils.encode(detail);
}
public void takeWatcherReward(int watcherId) {
var watcher = watcherInfoMap.get(watcherId);
if (watcher == null || watcher.isTakenReward()) {
return;
}
var reward =
Optional.of(watcher)
.map(WatcherInfo::getMetadata)
.map(ActivityWatcherData::getRewardID)
.map(id -> GameData.getRewardDataMap().get(id.intValue()));
if (reward.isEmpty()) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.get().getRewardItemList()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.ActivityWatcher);
watcher.setTakenReward(true);
save();
}
@Entity
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
public static class WatcherInfo {
int watcherId;
int totalProgress;
int curProgress;
boolean isTakenReward;
/**
* @return True when the progress of this watcher has reached the total progress.
*/
public boolean isFinished() {
return this.curProgress >= this.totalProgress;
}
public static WatcherInfo init(ActivityWatcher watcher) {
return WatcherInfo.of()
.watcherId(watcher.getWatcherId())
.totalProgress(watcher.getActivityWatcherData().getProgress())
.isTakenReward(false)
.build();
}
public ActivityWatcherData getMetadata() {
return GameData.getActivityWatcherDataMap().get(watcherId);
}
public ActivityWatcherInfoOuterClass.ActivityWatcherInfo toProto() {
return ActivityWatcherInfoOuterClass.ActivityWatcherInfo.newBuilder()
.setWatcherId(watcherId)
.setCurProgress(curProgress)
.setTotalProgress(totalProgress)
.setIsTakenReward(isTakenReward)
.build();
}
}
}

View File

@@ -1,16 +1,16 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link
* ActivityCondExcelConfigData}). To use it you should mark class that extends
* ActivityConditionBaseHandler, and it will be automatically picked during activity manager
* initiation.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityCondition {
ActivityConditions value();
}
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link
* ActivityCondExcelConfigData}). To use it you should mark class that extends
* ActivityConditionBaseHandler, and it will be automatically picked during activity manager
* initiation.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityCondition {
ActivityConditions value();
}

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