mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 03:05:34 +00:00
Create files from Grasscutter-Quests
these files are NOT directly compatible with Grasscutter, and require additional modifications to the codebase to work.
This commit is contained in:
parent
86aad96125
commit
262ee38ded
@ -0,0 +1,81 @@
|
|||||||
|
package emu.grasscutter.gen;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
import org.gradle.api.tasks.options.Option;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static java.lang.System.lineSeparator;
|
||||||
|
import static java.nio.file.Files.readAllLines;
|
||||||
|
import static java.nio.file.Files.writeString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task that can be used for generating/updating activity conditions enum. These
|
||||||
|
* activities come from Resources/ExcelBinOutput/NewActivityCondExcelConfigData.json
|
||||||
|
* resource file. Format file with formatter after this job is executed
|
||||||
|
* <br />
|
||||||
|
* Usage example: <i>./gradlew generateActivityConditions --conf-file=/Users/xxx/IdeaProjects/Grasscutter_Resources/Resources/ExcelBinOutput/NewActivityCondExcelConfigData.json</i>
|
||||||
|
*/
|
||||||
|
public class GenerateActivityConditions extends DefaultTask {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GenerateActivityConditions.class);
|
||||||
|
private static final String ACTIVITY_CONDITIONS_SRC = "/src/main/java/emu/grasscutter/game/activity/condition/ActivityConditions.java";
|
||||||
|
|
||||||
|
private static final String activityClassStart = """
|
||||||
|
package emu.grasscutter.game.activity;
|
||||||
|
|
||||||
|
public enum ActivityConditions {
|
||||||
|
""";
|
||||||
|
@Option(option = "conf-file", description = "Path to NewActivityCondExcelConfigData.json")
|
||||||
|
String confFile;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused") //Used by Gradle
|
||||||
|
public void setConfFile(String confFile) {
|
||||||
|
this.confFile = confFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void run() {
|
||||||
|
List<String> configFileContent = getFileContent(confFile);
|
||||||
|
|
||||||
|
Set<String> configEnums = configFileContent.stream()
|
||||||
|
.filter(s -> s.contains("\"type\":"))
|
||||||
|
.map(s -> s.split("\"")[3])
|
||||||
|
.map(s -> " " + s)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
String finalActivityClass =
|
||||||
|
activityClassStart +
|
||||||
|
String.join("," + lineSeparator(), configEnums) + lineSeparator() + "}";
|
||||||
|
|
||||||
|
writeFile(finalActivityClass, Path.of(getProject().getProjectDir() + ACTIVITY_CONDITIONS_SRC));
|
||||||
|
|
||||||
|
log.info("Successfully added {} enums to {}", configEnums.size(), ACTIVITY_CONDITIONS_SRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getFileContent(String path) {
|
||||||
|
try {
|
||||||
|
return readAllLines(Path.of(confFile));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Cannot read file: {}", path);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFile(String content, Path path) {
|
||||||
|
try {
|
||||||
|
writeString(path, content, StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Cannot read file: {}", path);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
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 lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static emu.grasscutter.command.CommandHelpers.*;
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
|
@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.callLuaHPEvent(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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
package emu.grasscutter.command.commands;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.Command;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
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.net.proto.ActivityInfoOuterClass.ActivityInfo;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
|
||||||
|
import emu.grasscutter.utils.JsonUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import static emu.grasscutter.utils.Language.translate;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
String action = args.get(0).toLowerCase();
|
||||||
|
String param = args.get(1);
|
||||||
|
|
||||||
|
val playerDataOption = targetPlayer.getActivityManager()
|
||||||
|
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
|
||||||
|
if (playerDataOption == null) {
|
||||||
|
CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
val playerData = playerDataOption.get();
|
||||||
|
if (playerData == null) {
|
||||||
|
CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var handler = (TrialAvatarActivityHandler) playerData.getActivityHandler();
|
||||||
|
if (handler == null) {
|
||||||
|
CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TrialAvatarPlayerData trialAvatarPlayerData = JsonUtils.decode(playerData.getDetail(), TrialAvatarPlayerData.class);
|
||||||
|
if (trialAvatarPlayerData == null) {
|
||||||
|
CommandHandler.sendMessage(sender, translate(sender, "commands.trialAvatarActivity.not_found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
default -> {
|
||||||
|
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())));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
package emu.grasscutter.data.binout.config;
|
||||||
|
|
||||||
|
public class ConfigEntityAvatar extends ConfigEntityBase {
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
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 lombok.Data;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigEntityBase {
|
||||||
|
@Nullable
|
||||||
|
ConfigCommon configCommon;
|
||||||
|
@Nullable
|
||||||
|
ConfigCombat combat;
|
||||||
|
Collection<ConfigAbilityData> abilities;
|
||||||
|
ConfigGlobalValue globalValue; // used for SGV in monsters and Gadgets
|
||||||
|
|
||||||
|
}
|
@ -0,0 +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
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.data.binout.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigEntityMonster extends ConfigEntityBase {
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package emu.grasscutter.data.binout.config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
|
||||||
|
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;
|
||||||
|
}
|
@ -0,0 +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;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package emu.grasscutter.data.binout.config.fields;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigAiBeta {
|
||||||
|
boolean enable;
|
||||||
|
}
|
@ -0,0 +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;
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
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;
|
||||||
|
}
|
@ -0,0 +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;
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.data.binout.config.fields;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConfigCommon {
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.data.binout.config.fields;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains information about the entities SGVs
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ConfigGlobalValue {
|
||||||
|
Set<String> serverGlobalValues;
|
||||||
|
Map<String, Float> initServerGlobalValues;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.data.binout.routes;
|
||||||
|
|
||||||
|
public enum RotType {
|
||||||
|
ROT_NONE,
|
||||||
|
ROT_ANGLE,
|
||||||
|
ROT_ROUND
|
||||||
|
}
|
31
src/main/java/emu/grasscutter/data/binout/routes/Route.java
Normal file
31
src/main/java/emu/grasscutter/data/binout/routes/Route.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
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.Getter;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.data.binout.routes;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
public class SceneRoutes {
|
||||||
|
private int sceneId;
|
||||||
|
@Nullable private Route[] routes;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package emu.grasscutter.data.common;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface BaseTrialActivityData {
|
||||||
|
List<Integer> getAvatarIndexIdList();
|
||||||
|
List<Integer> getRewardIdList();
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.data.custom;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.common.BaseTrialActivityData;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package emu.grasscutter.data.custom;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
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 lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
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.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
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 lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import emu.grasscutter.game.props.RefreshType;
|
||||||
|
import emu.grasscutter.game.world.World;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import emu.grasscutter.data.common.BaseTrialActivityData;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
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() {
|
||||||
|
triggerConfig.onLoad();
|
||||||
|
GameData.getTrialAvatarIndexIdTrialActivityDataDataMap().put(trialAvatarIndexId, id);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.data.excels;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameResource;
|
||||||
|
import emu.grasscutter.data.ResourceType;
|
||||||
|
import lombok.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package emu.grasscutter.data.server;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ActivityCondGroup {
|
||||||
|
int condGroupId;
|
||||||
|
List<Integer> condIds;
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.data.server;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class GadgetMapping {
|
||||||
|
private int gadgetId;
|
||||||
|
private String serverController;
|
||||||
|
}
|
34
src/main/java/emu/grasscutter/data/server/Grid.java
Normal file
34
src/main/java/emu/grasscutter/data/server/Grid.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.data.server;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.utils.GridPosition;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.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();
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base handler for all activity conditions that are listed in NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData})
|
||||||
|
*/
|
||||||
|
public abstract class ActivityConditionBaseHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute activity condition handler and return result of it's calculation
|
||||||
|
*
|
||||||
|
* @param activityData {@link PlayerActivityData} object containing info about activity
|
||||||
|
* @param activityConfig
|
||||||
|
* @param params params for handler
|
||||||
|
* @return result of condition calculation
|
||||||
|
*/
|
||||||
|
public abstract boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params);
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ActivityConditionExecutor {
|
||||||
|
|
||||||
|
List<Integer> getMeetActivitiesConditions(List<Integer> condIds);
|
||||||
|
|
||||||
|
boolean meetsCondition(int activityCondId);
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
public enum ActivityConditions {
|
||||||
|
NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_NOT_FINISH_TALK,
|
||||||
|
NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_PHOTO_POS_ID,
|
||||||
|
NEW_ACTIVITY_COND_HACHI_FINISH_STEALTH_STAGE_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_UNLOCKED_ALL_LISTED_SCENE_POINTS,
|
||||||
|
NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_BARTENDER_LEVEL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_HACHI_STAGE,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_ANY_INSTABLE_SPRAY_CHALLENGE_STAGE,
|
||||||
|
NEW_ACTIVITY_COND_HACHI_FINISH_BATTLE_STAGE_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_APPOINTED_STAGE_ALL_CAMP,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_WATCHER,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_REGION_SEARCH,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_WATER_SPIRIT_PHASE,
|
||||||
|
NEW_ACTIVITY_COND_SEA_LAMP_POPULARIT,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_DIG_ACTIVITY,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_FIND_HILICHURL_LEVEL_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_GACHA_CAN_CREATE_ROBOT,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_SALVAGE_STAGE,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_MUSIC_GAME_ALL_LEVEL,
|
||||||
|
NEW_ACTIVITY_COND_DAYS_LESS,
|
||||||
|
NEW_ACTIVITY_COND_QUEST_FINISH,
|
||||||
|
NEW_ACTIVITY_COND_QUEST_GLOBAL_VAR_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_GROUP_BUNDLE_FINISHED,
|
||||||
|
NEW_ACTIVITY_COND_SEA_LAMP_PHASE,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ANY_STAGE_ALL_CAMP,
|
||||||
|
NEW_ACTIVITY_COND_LUMINANCE_STONE_CHALLENGE_FINAL_GALLERY_COMPLETE,
|
||||||
|
NEW_ACTIVITY_COND_PLANT_FLOWER_CAN_DELIVER,
|
||||||
|
NEW_ACTIVITY_COND_LUMINANCE_STONE_CHALLENGE_STAGE_GREAT_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_ANY_ARENA_CHALLENGE_LEVEL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_CUSTOM_DUNGEON_OFFICIAL,
|
||||||
|
NEW_ACTIVITY_COND_SCENE_MP_PLAY_ACTIVATED,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_FIND_HILICHURL_LEVEL_LESS,
|
||||||
|
NEW_ACTIVITY_COND_TIME_GREATER,
|
||||||
|
NEW_ACTIVITY_COND_CREATE_NPC,
|
||||||
|
NEW_ACTIVITY_COND_TREASURE_SEELIE_FINISH_REGION,
|
||||||
|
NEW_ACTIVITY_COND_LUNA_RITE_ATMOSPHERE,
|
||||||
|
NEW_ACTIVITY_COND_OFFERING_LEVEL_GREAT_EQUAL,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ANY_ONEOFF_DUNGEON,
|
||||||
|
NEW_ACTIVITY_COND_QUEST_FINISH_ALLOW_QUICK_OPEN,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_POTION_ANY_LEVEL,
|
||||||
|
NEW_ACTIVITY_COND_MECHANICUS_OPEN,
|
||||||
|
NEW_ACTIVITY_COND_PLAYER_LEVEL_GREATER,
|
||||||
|
NEW_ACTIVITY_COND_SALESMAN_CAN_GET_REWARD,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_REGION_SEARCH_LOGIC,
|
||||||
|
NEW_ACTIVITY_COND_FINISH_CHANNELLER_SLAB_ONEOFF_DUNGEON_IN_STAGE
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
|
||||||
|
import emu.grasscutter.game.quest.handlers.QuestBaseHandler;
|
||||||
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that used for scanning classpath, picking up all activity conditions (for NewActivityCondExcelConfigData.json {@link ActivityCondExcelConfigData})
|
||||||
|
* and saving them to map. Check for more info {@link ActivityCondition}
|
||||||
|
*/
|
||||||
|
public class AllActivityConditionBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build activity conditions handlers
|
||||||
|
*
|
||||||
|
* @return map containing all condition handlers for NewActivityCondExcelConfigData.json
|
||||||
|
*/
|
||||||
|
static public Map<ActivityConditions, ActivityConditionBaseHandler> buildActivityConditions() {
|
||||||
|
return new AllActivityConditionBuilder().initActivityConditions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<ActivityConditions, ActivityConditionBaseHandler> initActivityConditions() {
|
||||||
|
Reflections reflector = Grasscutter.reflector;
|
||||||
|
return reflector.getTypesAnnotatedWith(ActivityCondition.class).stream()
|
||||||
|
.map(this::newInstance)
|
||||||
|
.map(h -> new AbstractMap.SimpleEntry<>(extractActionType(h), h))
|
||||||
|
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivityConditions extractActionType(ActivityConditionBaseHandler e) {
|
||||||
|
ActivityCondition condition = e.getClass().getAnnotation(ActivityCondition.class);
|
||||||
|
if (condition == null) {
|
||||||
|
Grasscutter.getLogger().error("Failed to read command type for class {}", e.getClass().getName());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return condition.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivityConditionBaseHandler newInstance(Class<?> clazz) {
|
||||||
|
try {
|
||||||
|
Object result = clazz.getDeclaredConstructor().newInstance();
|
||||||
|
if (result instanceof ActivityConditionBaseHandler) {
|
||||||
|
return (ActivityConditionBaseHandler) result;
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().error("Failed to initiate activity condition: {}, object have wrong type", clazz.getName());
|
||||||
|
} catch (Exception e) {
|
||||||
|
String message = String.format("Failed to initiate activity condition: %s, %s", clazz.getName(), e.getMessage());
|
||||||
|
Grasscutter.getLogger().error(message, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.all.UnknownActivityConditionHandler;
|
||||||
|
import emu.grasscutter.game.quest.enums.LogicType;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class BasicActivityConditionExecutor implements ActivityConditionExecutor {
|
||||||
|
|
||||||
|
private final Map<Integer, ActivityConfigItem> activityConfigItemMap;
|
||||||
|
private final Int2ObjectMap<ActivityCondExcelConfigData> activityConditions;
|
||||||
|
|
||||||
|
private final Int2ObjectMap<PlayerActivityData> playerActivityDataByActivityCondId;
|
||||||
|
private final Map<ActivityConditions, ActivityConditionBaseHandler> activityConditionsHandlers;
|
||||||
|
|
||||||
|
private static final UnknownActivityConditionHandler UNKNOWN_CONDITION_HANDLER = new UnknownActivityConditionHandler();
|
||||||
|
|
||||||
|
public BasicActivityConditionExecutor(Map<Integer, ActivityConfigItem> activityConfigItemMap,
|
||||||
|
Int2ObjectMap<ActivityCondExcelConfigData> activityConditions,
|
||||||
|
Int2ObjectMap<PlayerActivityData> playerActivityDataByActivityCondId,
|
||||||
|
Map<ActivityConditions, ActivityConditionBaseHandler> activityConditionsHandlers) {
|
||||||
|
this.activityConfigItemMap = activityConfigItemMap;
|
||||||
|
this.activityConditions = activityConditions;
|
||||||
|
this.playerActivityDataByActivityCondId = playerActivityDataByActivityCondId;
|
||||||
|
this.activityConditionsHandlers = activityConditionsHandlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Integer> getMeetActivitiesConditions(List<Integer> condIds) {
|
||||||
|
return condIds.stream()
|
||||||
|
.filter(this::meetsCondition)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean meetsCondition(int activityCondId) {
|
||||||
|
ActivityCondExcelConfigData condData = activityConditions.get(activityCondId);
|
||||||
|
|
||||||
|
if (condData == null) {
|
||||||
|
Grasscutter.getLogger().error("Could not find condition for activity with id = {}", activityCondId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogicType condComb = condData.getCondComb();
|
||||||
|
if (condComb == null) {
|
||||||
|
condComb = LogicType.LOGIC_AND;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerActivityData activity = playerActivityDataByActivityCondId.get(activityCondId);
|
||||||
|
if(activity==null){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ActivityConfigItem activityConfig = activityConfigItemMap.get(activity.getActivityId());
|
||||||
|
List<BooleanSupplier> predicates = condData.getCond()
|
||||||
|
.stream()
|
||||||
|
.map(c -> (BooleanSupplier) () ->
|
||||||
|
activityConditionsHandlers
|
||||||
|
.getOrDefault(c.getType(), UNKNOWN_CONDITION_HANDLER).execute(activity, activityConfig, c.paramArray()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return LogicType.calculate(condComb, predicates);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap.BasicEntry;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used for building mapping for PlayerActivityData
|
||||||
|
*/
|
||||||
|
public class PlayerActivityDataMappingBuilder {
|
||||||
|
|
||||||
|
|
||||||
|
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
|
||||||
|
|
||||||
|
private final Int2ObjectMap<ActivityCondExcelConfigData> activityCondMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build mapping for PlayerActivityData.
|
||||||
|
*
|
||||||
|
* @return mapping for activity data. Key is condId from NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}) resource,
|
||||||
|
* value is {@link PlayerActivityData} class for related activity.
|
||||||
|
*/
|
||||||
|
public static Int2ObjectMap<PlayerActivityData> buildPlayerActivityDataByActivityCondId(Map<Integer, PlayerActivityData> activities) {
|
||||||
|
return new PlayerActivityDataMappingBuilder(activities).buildMappings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerActivityDataMappingBuilder(Map<Integer, PlayerActivityData> playerActivityDataMap) {
|
||||||
|
this.playerActivityDataMap = playerActivityDataMap;
|
||||||
|
activityCondMap = GameData.getActivityCondExcelConfigDataMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Int2ObjectMap<PlayerActivityData> buildMappings() {
|
||||||
|
Int2ObjectMap<PlayerActivityData> result = new Int2ObjectRBTreeMap<>();
|
||||||
|
|
||||||
|
activityCondMap
|
||||||
|
.int2ObjectEntrySet()
|
||||||
|
.stream()
|
||||||
|
.map(entry -> new BasicEntry<>(entry.getIntKey(), getPlayerActivityDataByCondId(entry.getIntKey())))
|
||||||
|
.filter(entry -> entry.getValue() != null)
|
||||||
|
.forEach(entry -> result.put(entry.getIntKey(), entry.getValue()));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerActivityData getPlayerActivityDataByCondId(Integer key) {
|
||||||
|
return playerActivityDataMap.get(detectActivityDataIdByCondId(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect activity data id by cond id. Cond id comes from condId field from NewActivityCondExcelConfigData.json.
|
||||||
|
* See {@link ActivityCondExcelConfigData} for condId.
|
||||||
|
* <p>
|
||||||
|
* Generally, there are 3 cases:
|
||||||
|
* <ol>
|
||||||
|
* <li>Activity data id >= 5003. Then cond id will be activity data id plus 3 additional digits.
|
||||||
|
* For example: activity data id = 5087, cond id = 5087xxx (x - any digit)</li>
|
||||||
|
* <li>Activity data id = 5001. Then cond id will be activity data id plus 2 additional digits.
|
||||||
|
* For example: activity data id = 5001, cond id = 5001xx (x - any digit)</li>
|
||||||
|
* <li>Activity data id one of [1001]. Then cond id will be activity data id plus 2 additional digits.
|
||||||
|
* This also applied to activity data id = 1002. For example: activity data id = 1001, cond id = 1001x (x - any digit></li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @param key cond id for which activity data id should be defined
|
||||||
|
* @return activity data for given cond id. Returns -1 if activity was not found.
|
||||||
|
*/
|
||||||
|
private Integer detectActivityDataIdByCondId(Integer key) {
|
||||||
|
if (key / 10 == 1001 || key / 10 == 1002) {
|
||||||
|
return 1001;
|
||||||
|
} else if (key / 100 == 5001) {
|
||||||
|
return key / 100;
|
||||||
|
} else {
|
||||||
|
return key / 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_LESS;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_DAYS_LESS)
|
||||||
|
public class DayLess extends ActivityConditionBaseHandler {
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
return true; //TODO implement this and add possibility to always return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL)
|
||||||
|
public class DaysGreatEqual extends ActivityConditionBaseHandler {
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
Date activityBeginTime = activityConfig.getBeginTime();
|
||||||
|
long timeDiff = System.currentTimeMillis() - activityBeginTime.getTime();
|
||||||
|
int days = (int) (timeDiff / (1000 * 60 * 60 * 24L));
|
||||||
|
return days + 1 >= params[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditions;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
@ActivityCondition(ActivityConditions.NEW_ACTIVITY_COND_FINISH_WATCHER)
|
||||||
|
public class FinishWatcher extends ActivityConditionBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
val watcherMap = activityData.getWatcherInfoMap();
|
||||||
|
for (int param : params) {
|
||||||
|
val watcher = watcherMap.get(param);
|
||||||
|
if(watcher == null || !watcher.isFinished()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_NOT_FINISH_TALK;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_NOT_FINISH_TALK)
|
||||||
|
public class NotFinishTalk extends ActivityConditionBaseHandler {
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
return activityData
|
||||||
|
.getPlayer()
|
||||||
|
.getQuestManager()
|
||||||
|
.getMainQuests()
|
||||||
|
.int2ObjectEntrySet()
|
||||||
|
.stream()
|
||||||
|
.noneMatch(q -> q.getValue().getTalks().get(params[0]) != null); //FIXME taken from ContentCompleteTalk
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL)
|
||||||
|
public class PlayerLevelGreatEqualActivityActivityCondition extends ActivityConditionBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
return activityData.getPlayer().getLevel() >= params[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
import emu.grasscutter.game.quest.GameQuest;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestState;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_QUEST_FINISH;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_QUEST_FINISH)
|
||||||
|
public class QuestFinished extends ActivityConditionBaseHandler {
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
GameQuest quest = activityData
|
||||||
|
.getPlayer()
|
||||||
|
.getQuestManager()
|
||||||
|
.getQuestById(params[0]);
|
||||||
|
|
||||||
|
return quest != null && quest.getState() == QuestState.QUEST_STATE_FINISHED;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityCondition;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER;
|
||||||
|
|
||||||
|
@ActivityCondition(NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER)
|
||||||
|
public class SalesmanCanDeliver extends ActivityConditionBaseHandler {
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
//TODO need to reverse engineer this logic.
|
||||||
|
//This condition appears only in one condition "condId": 5003001
|
||||||
|
//and this condition accept no params. I have no idea how to implement it
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.game.activity.condition.all;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.activity.ActivityConfigItem;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used when condition was not found
|
||||||
|
*/
|
||||||
|
public class UnknownActivityConditionHandler extends ActivityConditionBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
|
||||||
|
Grasscutter.getLogger().error("Called unknown condition handler");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package emu.grasscutter.game.activity.trialavatar;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||||
|
import emu.grasscutter.game.activity.ActivityWatcherType;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
|
||||||
|
import lombok.val;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE)
|
||||||
|
public class TrialAvatarActivityChallengeTrigger extends ActivityWatcher {
|
||||||
|
@Override
|
||||||
|
protected boolean isMeet(String... param) {
|
||||||
|
if(param.length < 3) return false;
|
||||||
|
|
||||||
|
val handler = (TrialAvatarActivityHandler) getActivityHandler();
|
||||||
|
if(handler == null) return false;
|
||||||
|
|
||||||
|
val paramList = handler.getTriggerParamList();
|
||||||
|
if(paramList.isEmpty()) return false;
|
||||||
|
|
||||||
|
val paramCond = Stream.of(paramList.get(0).split(",")).toList();
|
||||||
|
return Stream.of(param).allMatch(x -> paramCond.contains(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void trigger(PlayerActivityData playerActivityData, String... param) {
|
||||||
|
if (!isMeet(param)) return;
|
||||||
|
|
||||||
|
val handler = (TrialAvatarActivityHandler) getActivityHandler();
|
||||||
|
if(handler == null) return;
|
||||||
|
|
||||||
|
handler.setPassDungeon(playerActivityData);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
package emu.grasscutter.game.activity.trialavatar;
|
||||||
|
|
||||||
|
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.excels.RewardData;
|
||||||
|
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||||
|
import emu.grasscutter.game.activity.DefaultWatcher;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonTrialTeam;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.game.activity.ActivityHandler;
|
||||||
|
import emu.grasscutter.game.activity.GameActivity;
|
||||||
|
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||||
|
import emu.grasscutter.game.props.ActivityType;
|
||||||
|
import emu.grasscutter.net.proto.ActivityInfoOuterClass.ActivityInfo;
|
||||||
|
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify;
|
||||||
|
import emu.grasscutter.utils.JsonUtils;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@GameActivity(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR)
|
||||||
|
public class TrialAvatarActivityHandler extends ActivityHandler {
|
||||||
|
@Getter @Setter private int selectedTrialAvatarIndex;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
|
||||||
|
TrialAvatarPlayerData trialAvatarPlayerData = TrialAvatarPlayerData.create(getActivityConfigItem().getScheduleId());
|
||||||
|
|
||||||
|
playerActivityData.setDetail(trialAvatarPlayerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProtoBuild(PlayerActivityData playerActivityData, ActivityInfo.Builder activityInfo) {
|
||||||
|
TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData);
|
||||||
|
|
||||||
|
activityInfo.setTrialAvatarInfo(trialAvatarPlayerData.toProto());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initWatchers(Map<WatcherTriggerType, ConstructorAccess<?>> activityWatcherTypeMap) {
|
||||||
|
var watcherType = activityWatcherTypeMap.get(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE);
|
||||||
|
ActivityWatcher watcher;
|
||||||
|
if(watcherType != null){
|
||||||
|
watcher = (ActivityWatcher) watcherType.newInstance();
|
||||||
|
}else{
|
||||||
|
watcher = new DefaultWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher.setActivityHandler(this);
|
||||||
|
getWatchersMap().computeIfAbsent(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE, k -> new ArrayList<>());
|
||||||
|
getWatchersMap().get(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE).add(watcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrialAvatarPlayerData getTrialAvatarPlayerData(PlayerActivityData playerActivityData) {
|
||||||
|
if (playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()) {
|
||||||
|
onInitPlayerActivityData(playerActivityData);
|
||||||
|
playerActivityData.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonUtils.decode(playerActivityData.getDetail(), TrialAvatarPlayerData.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTrialActivityDungeonId(int trialAvatarIndexId) {
|
||||||
|
val data = GameData.getTrialAvatarActivityDataByAvatarIndex(trialAvatarIndexId);
|
||||||
|
return data!=null ? data.getDungeonId() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getTriggerParamList() {
|
||||||
|
val data = GameData.getTrialAvatarActivityDataByAvatarIndex(getSelectedTrialAvatarIndex());
|
||||||
|
return data!=null ? data.getTriggerConfig().getParamList() : Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean enterTrialDungeon(Player player, int trialAvatarIndexId, int enterPointId) {
|
||||||
|
// TODO, not sure if this will cause problem in MP, since we are entering trial activity dungeon
|
||||||
|
player.sendPacket(new PacketScenePlayerLocationNotify(player.getScene())); // official does send this
|
||||||
|
|
||||||
|
if (!player.getServer().getDungeonSystem().enterDungeon(
|
||||||
|
player,
|
||||||
|
enterPointId,
|
||||||
|
getTrialActivityDungeonId(trialAvatarIndexId))) return false;
|
||||||
|
|
||||||
|
setSelectedTrialAvatarIndex(trialAvatarIndexId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getBattleAvatarsList() {
|
||||||
|
val activityData = GameData.getTrialAvatarActivityDataByAvatarIndex(getSelectedTrialAvatarIndex());
|
||||||
|
if (activityData == null || activityData.getBattleAvatarsList().isBlank()) return List.of();
|
||||||
|
return Stream.of(activityData.getBattleAvatarsList().split(",")).map(Integer::parseInt).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DungeonTrialTeam getTrialAvatarDungeonTeam(){
|
||||||
|
List<Integer> battleAvatarsList = getBattleAvatarsList();
|
||||||
|
if (battleAvatarsList.isEmpty()) return null;
|
||||||
|
|
||||||
|
return new DungeonTrialTeam(battleAvatarsList, GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsetTrialAvatarTeam(Player player) {
|
||||||
|
if (getSelectedTrialAvatarIndex() <= 0) return;
|
||||||
|
player.removeTrialAvatarForActivity();
|
||||||
|
setSelectedTrialAvatarIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getReward(Player player, int trialAvatarIndexId) {
|
||||||
|
val playerActivityData = player.getActivityManager().getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
|
||||||
|
|
||||||
|
if(playerActivityData.isEmpty()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData.get());
|
||||||
|
TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfo(trialAvatarIndexId);
|
||||||
|
if (rewardInfo == null) return false;
|
||||||
|
|
||||||
|
RewardData rewardParam = GameData.getRewardDataMap().get(rewardInfo.getRewardId());
|
||||||
|
if (rewardParam == null) return false;
|
||||||
|
|
||||||
|
player.getInventory().addItemParamDatas(rewardParam.getRewardItemList(), ActionReason.TrialAvatarActivityFirstPassReward);
|
||||||
|
rewardInfo.setReceivedReward(true);
|
||||||
|
playerActivityData.get().setDetail(trialAvatarPlayerData);
|
||||||
|
playerActivityData.get().save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassDungeon(PlayerActivityData playerActivityData) {
|
||||||
|
TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData);
|
||||||
|
TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfo(getSelectedTrialAvatarIndex());
|
||||||
|
if (rewardInfo == null) return;
|
||||||
|
|
||||||
|
rewardInfo.setPassedDungeon(true);
|
||||||
|
playerActivityData.setDetail(trialAvatarPlayerData);
|
||||||
|
playerActivityData.save();
|
||||||
|
Player player = Grasscutter.getGameServer().getPlayerByUid(playerActivityData.getUid());
|
||||||
|
player.sendPacket(new PacketActivityInfoNotify(toProto(playerActivityData, player.getActivityManager().getConditionExecutor())));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
package emu.grasscutter.game.activity.trialavatar;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.common.BaseTrialActivityData;
|
||||||
|
import emu.grasscutter.net.proto.TrialAvatarActivityDetailInfoOuterClass.TrialAvatarActivityDetailInfo;
|
||||||
|
import emu.grasscutter.net.proto.TrialAvatarActivityRewardDetailInfoOuterClass.TrialAvatarActivityRewardDetailInfo;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.FieldDefaults;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
@Builder(builderMethodName = "of")
|
||||||
|
public class TrialAvatarPlayerData {
|
||||||
|
List<RewardInfoItem> rewardInfoList;
|
||||||
|
|
||||||
|
private static BaseTrialActivityData getActivityData(int scheduleId){
|
||||||
|
// prefer custom data over official data
|
||||||
|
return GameData.getTrialAvatarActivityCustomData().isEmpty() ? GameData.getTrialAvatarActivityDataMap().get(scheduleId)
|
||||||
|
: GameData.getTrialAvatarActivityCustomData().get(scheduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> getAvatarIdList(int scheduleId) {
|
||||||
|
val activityData = getActivityData(scheduleId);
|
||||||
|
return activityData != null ? activityData.getAvatarIndexIdList() : List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> getRewardIdList(int scheduleId) {
|
||||||
|
val activityData = getActivityData(scheduleId);
|
||||||
|
return activityData != null ? activityData.getRewardIdList() : List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TrialAvatarPlayerData create(int scheduleId) {
|
||||||
|
List<Integer> avatarIds = getAvatarIdList(scheduleId);
|
||||||
|
List<Integer> rewardIds = getRewardIdList(scheduleId);
|
||||||
|
return TrialAvatarPlayerData.of()
|
||||||
|
.rewardInfoList(IntStream.range(0, avatarIds.size())
|
||||||
|
.filter(i -> avatarIds.get(i) > 0 && rewardIds.get(i) > 0)
|
||||||
|
.mapToObj(i -> RewardInfoItem.create(
|
||||||
|
avatarIds.get(i),
|
||||||
|
rewardIds.get(i)))
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrialAvatarActivityDetailInfo toProto() {
|
||||||
|
return TrialAvatarActivityDetailInfo.newBuilder()
|
||||||
|
.addAllRewardInfoList(getRewardInfoList().stream()
|
||||||
|
.map(RewardInfoItem::toProto).toList())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RewardInfoItem getRewardInfo(int trialAvatarIndexId) {
|
||||||
|
return getRewardInfoList().stream().filter(x -> x.getTrialAvatarIndexId() == trialAvatarIndexId)
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||||
|
@Builder(builderMethodName = "of")
|
||||||
|
public static class RewardInfoItem {
|
||||||
|
int trialAvatarIndexId;
|
||||||
|
int rewardId;
|
||||||
|
boolean passedDungeon;
|
||||||
|
boolean receivedReward;
|
||||||
|
|
||||||
|
public static RewardInfoItem create(int trialAvatarIndexId, int rewardId) {
|
||||||
|
return RewardInfoItem.of()
|
||||||
|
.trialAvatarIndexId(trialAvatarIndexId)
|
||||||
|
.rewardId(rewardId)
|
||||||
|
.passedDungeon(false)
|
||||||
|
.receivedReward(false)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrialAvatarActivityRewardDetailInfo toProto() {
|
||||||
|
return TrialAvatarActivityRewardDetailInfo.newBuilder()
|
||||||
|
.setTrialAvatarIndexId(getTrialAvatarIndexId())
|
||||||
|
.setRewardId(getRewardId())
|
||||||
|
.setPassedDungeon(isPassedDungeon())
|
||||||
|
.setReceivedReward(isReceivedReward())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class DungeonEndStats {
|
||||||
|
@Getter private int killedMonsters;
|
||||||
|
@Getter private int timeTaken;
|
||||||
|
@Getter private int openChestCount;
|
||||||
|
@Getter private BaseDungeonResult.DungeonEndReason dungeonResult;
|
||||||
|
|
||||||
|
public DungeonEndStats(int killedMonsters, int timeTaken, int openChestCount, BaseDungeonResult.DungeonEndReason dungeonResult){
|
||||||
|
this.killedMonsters = killedMonsters;
|
||||||
|
this.timeTaken = timeTaken;
|
||||||
|
this.dungeonResult = dungeonResult;
|
||||||
|
this.openChestCount = openChestCount;
|
||||||
|
}
|
||||||
|
}
|
315
src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java
Normal file
315
src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
|
||||||
|
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.game.props.ActivityType;
|
||||||
|
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||||
|
import emu.grasscutter.game.quest.enums.LogicType;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketDungeonWayPointNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO handle time limits
|
||||||
|
* TODO handle respawn points
|
||||||
|
* TODO handle team wipes and respawns
|
||||||
|
* TODO check monster level and levelConfigMap
|
||||||
|
*/
|
||||||
|
public class DungeonManager {
|
||||||
|
|
||||||
|
@Getter private final Scene scene;
|
||||||
|
@Getter private final DungeonData dungeonData;
|
||||||
|
@Getter private final DungeonPassConfigData passConfigData;
|
||||||
|
|
||||||
|
@Getter private final int[] finishedConditions;
|
||||||
|
private final IntSet rewardedPlayers = new IntOpenHashSet();
|
||||||
|
private final Set<Integer> activeDungeonWayPoints = new HashSet<>();
|
||||||
|
private boolean ended = false;
|
||||||
|
private int newestWayPoint = 0;
|
||||||
|
@Getter private int startSceneTime = 0;
|
||||||
|
|
||||||
|
DungeonTrialTeam trialTeam = null;
|
||||||
|
|
||||||
|
public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) {
|
||||||
|
this.scene = scene;
|
||||||
|
this.dungeonData = dungeonData;
|
||||||
|
this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond());
|
||||||
|
this.finishedConditions = new int[passConfigData.getConds().size()];
|
||||||
|
this.scene.setDungeonManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void triggerEvent(DungeonPassConditionType conditionType, int... params) {
|
||||||
|
if (ended) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < passConfigData.getConds().size(); i++) {
|
||||||
|
var cond = passConfigData.getConds().get(i);
|
||||||
|
if (conditionType == cond.getCondType()) {
|
||||||
|
if (getScene().getWorld().getServer().getDungeonSystem().triggerCondition(cond, params)) {
|
||||||
|
finishedConditions[i] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFinishedSuccessfully()) {
|
||||||
|
finishDungeon();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFinishedSuccessfully() {
|
||||||
|
return LogicType.calculate(passConfigData.getLogicType(), finishedConditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLevelForMonster(int id) {
|
||||||
|
//TODO should use levelConfigMap? and how?
|
||||||
|
return dungeonData.getShowLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean activateRespawnPoint(int pointId) {
|
||||||
|
val respawnPoint = GameData.getScenePointEntryById(scene.getId(), pointId);
|
||||||
|
|
||||||
|
if (respawnPoint == null) {
|
||||||
|
Grasscutter.getLogger().warn("trying to activate unknown respawn point {}", pointId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.broadcastPacket(new PacketDungeonWayPointNotify(activeDungeonWayPoints.add(pointId), activeDungeonWayPoints));
|
||||||
|
newestWayPoint = pointId;
|
||||||
|
|
||||||
|
Grasscutter.getLogger().debug("[unimplemented respawn] activated respawn point {}", pointId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Position getRespawnLocation() {
|
||||||
|
if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData();
|
||||||
|
return pointData.getTranPos() != null ? pointData.getTranPos() : pointData.getPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Position getRespawnRotation() {
|
||||||
|
if (newestWayPoint == 0) { // validity is checked before setting it, so if != 0 its always valid
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
val pointData = GameData.getScenePointEntryById(scene.getId(), newestWayPoint).getPointData();
|
||||||
|
return pointData.getRot() != null ? pointData.getRot() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getStatueDrops(Player player, boolean useCondensed, int groupId) {
|
||||||
|
if (!isFinishedSuccessfully() || dungeonData.getRewardPreviewData() == null || dungeonData.getRewardPreviewData().getPreviewItems().length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already rewarded
|
||||||
|
if (rewardedPlayers.contains(player.getUid())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!handleCost(player, useCondensed)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get and roll rewards.
|
||||||
|
List<GameItem> rewards = new ArrayList<>(this.rollRewards(useCondensed));
|
||||||
|
// Add rewards to player and send notification.
|
||||||
|
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||||
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
rewardedPlayers.add(player.getUid());
|
||||||
|
|
||||||
|
scene.getScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_DUNGEON_REWARD_GET));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleCost(Player player, boolean useCondensed) {
|
||||||
|
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||||
|
if (resinCost == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (useCondensed) {
|
||||||
|
// Check if condensed resin is usable here.
|
||||||
|
// For this, we use the following logic for now:
|
||||||
|
// The normal resin cost of the dungeon has to be 20.
|
||||||
|
if (resinCost != 20) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spend the condensed resin and only proceed if the transaction succeeds.
|
||||||
|
return player.getResinManager().useCondensedResin(1);
|
||||||
|
} else if (dungeonData.getStatueCostID() == 106) {
|
||||||
|
// Spend the resin and only proceed if the transaction succeeds.
|
||||||
|
return player.getResinManager().useResin(resinCost);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<GameItem> rollRewards(boolean useCondensed) {
|
||||||
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
int dungeonId = this.dungeonData.getId();
|
||||||
|
// If we have specific drop data for this dungeon, we use it.
|
||||||
|
if (GameData.getDungeonDropDataMap().containsKey(dungeonId)) {
|
||||||
|
List<DungeonDropEntry> dropEntries = GameData.getDungeonDropDataMap().get(dungeonId);
|
||||||
|
|
||||||
|
// Roll for each drop group.
|
||||||
|
for (var entry : dropEntries) {
|
||||||
|
// Determine the number of drops we get for this entry.
|
||||||
|
int start = entry.getCounts().get(0);
|
||||||
|
int end = entry.getCounts().get(entry.getCounts().size() - 1);
|
||||||
|
var candidateAmounts = IntStream.range(start, end + 1).boxed().collect(Collectors.toList());
|
||||||
|
|
||||||
|
int amount = Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||||
|
|
||||||
|
if (useCondensed) {
|
||||||
|
amount += Utils.drawRandomListElement(candidateAmounts, entry.getProbabilities());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double rewards in multiplay mode, if specified.
|
||||||
|
if (entry.isMpDouble() && this.getScene().getPlayerCount() > 1) {
|
||||||
|
amount *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll items for this group.
|
||||||
|
// Here, we have to handle stacking, or the client will not display results correctly.
|
||||||
|
// For now, we use the following logic: If the possible drop item are a list of multiple items,
|
||||||
|
// we roll them separately. If not, we stack them. This should work out in practice, at least
|
||||||
|
// for the currently existing set of dungeons.
|
||||||
|
if (entry.getItems().size() == 1) {
|
||||||
|
rewards.add(new GameItem(entry.getItems().get(0), amount));
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < amount; i++) {
|
||||||
|
// int itemIndex = ThreadLocalRandom.current().nextInt(0, entry.getItems().size());
|
||||||
|
// int itemId = entry.getItems().get(itemIndex);
|
||||||
|
int itemId = Utils.drawRandomListElement(entry.getItems(), entry.getItemProbabilities());
|
||||||
|
rewards.add(new GameItem(itemId, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, we fall back to the preview data.
|
||||||
|
else {
|
||||||
|
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
|
||||||
|
for (ItemParamData param : dungeonData.getRewardPreviewData().getPreviewItems()) {
|
||||||
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void applyTrialTeam(Player player) {
|
||||||
|
if (getDungeonData() == null) return;
|
||||||
|
|
||||||
|
switch (getDungeonData().getType()) {
|
||||||
|
// case DUNGEON_PLOT is handled by quest execs
|
||||||
|
case DUNGEON_ACTIVITY -> {
|
||||||
|
switch (getDungeonData().getPlayType()) {
|
||||||
|
case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> {
|
||||||
|
val activityHandler = player.getActivityManager()
|
||||||
|
.getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class);
|
||||||
|
activityHandler.ifPresent(trialAvatarActivityHandler ->
|
||||||
|
this.trialTeam = trialAvatarActivityHandler.getTrialAvatarDungeonTeam());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case DUNGEON_ELEMENT_CHALLENGE -> {} // TODO
|
||||||
|
}
|
||||||
|
if(this.trialTeam != null) {
|
||||||
|
player.addTrialAvatarsForActivity(trialTeam.trialAvatarIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsetTrialTeam(Player player){
|
||||||
|
if(this.trialTeam==null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.removeTrialAvatarForActivity();
|
||||||
|
this.trialTeam = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startDungeon() {
|
||||||
|
this.startSceneTime = scene.getSceneTimeSeconds();
|
||||||
|
scene.getPlayers().forEach(p -> {
|
||||||
|
p.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, dungeonData.getId());
|
||||||
|
applyTrialTeam(p);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void finishDungeon() {
|
||||||
|
notifyEndDungeon(true);
|
||||||
|
endDungeon(BaseDungeonResult.DungeonEndReason.COMPLETED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notifyEndDungeon(boolean successfully) {
|
||||||
|
scene.getPlayers().forEach(p -> {
|
||||||
|
// Quest trigger
|
||||||
|
p.getQuestManager().queueEvent(successfully ?
|
||||||
|
QuestContent.QUEST_CONTENT_FINISH_DUNGEON : QuestContent.QUEST_CONTENT_FAIL_DUNGEON,
|
||||||
|
dungeonData.getId());
|
||||||
|
|
||||||
|
// Battle pass trigger
|
||||||
|
if (dungeonData.getType().isCountsToBattlepass() && successfully) {
|
||||||
|
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
scene.getScriptManager().callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void quitDungeon() {
|
||||||
|
notifyEndDungeon(false);
|
||||||
|
endDungeon(BaseDungeonResult.DungeonEndReason.QUIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void failDungeon() {
|
||||||
|
notifyEndDungeon(false);
|
||||||
|
endDungeon(BaseDungeonResult.DungeonEndReason.FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) {
|
||||||
|
if (scene.getDungeonSettleListeners() != null) {
|
||||||
|
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason));
|
||||||
|
}
|
||||||
|
ended = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restartDungeon() {
|
||||||
|
this.scene.setKilledMonsterCount(0);
|
||||||
|
this.rewardedPlayers.clear();
|
||||||
|
Arrays.fill(finishedConditions, 0);
|
||||||
|
this.ended = false;
|
||||||
|
this.activeDungeonWayPoints.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanUpScene() {
|
||||||
|
this.scene.setDungeonManager(null);
|
||||||
|
this.scene.setKilledMonsterCount(0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class DungeonTrialTeam {
|
||||||
|
List<Integer> trialAvatarIds;
|
||||||
|
TrialAvatarGrantRecord.GrantReason grantReason;
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package emu.grasscutter.game.dungeons;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface DungeonValue {
|
||||||
|
DungeonPassConditionType value();
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||||
|
|
||||||
|
public enum ChallengeCondType {
|
||||||
|
CHALLENGE_COND_NONE, //00
|
||||||
|
CHALLENGE_COND_IN_TIME, //01
|
||||||
|
CHALLENGE_COND_ALL_TIME, //02
|
||||||
|
CHALLENGE_COND_KILL_COUNT, //03
|
||||||
|
CHALLENGE_COND_SURVIVE, //04
|
||||||
|
CHALLENGE_COND_TIME_INC, //05
|
||||||
|
CHALLENGE_COND_KILL_FAST, //06
|
||||||
|
CHALLENGE_COND_DOWN_LESS, //07
|
||||||
|
CHALLENGE_COND_BEATEN_LESS , //08
|
||||||
|
CHALLENGE_COND_UNNATURAL_COUNT , //09
|
||||||
|
CHALLENGE_COND_FROZEN_LESS , //10
|
||||||
|
CHALLENGE_COND_KILL_MONSTER , //11
|
||||||
|
CHALLENGE_COND_TRIGGER , //12
|
||||||
|
CHALLENGE_COND_GUARD_HP , //13
|
||||||
|
CHALLENGE_COND_TIME_DEC , //14
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||||
|
|
||||||
|
public enum ChallengeEventMarkType {
|
||||||
|
CHALLENGE_EVENT_NONE,
|
||||||
|
FLIGHT_TIME,
|
||||||
|
FLIGHT_GATHER_POINT,
|
||||||
|
SUMMER_TIME_SPRINT_BOAT_TIME,
|
||||||
|
SUMMER_TIME_SPRINT_BOAT_GATHER_POINT,
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||||
|
|
||||||
|
public enum ChallengeRecordType {
|
||||||
|
CHALLENGE_RECORD_TYPE_NONE,
|
||||||
|
CHALLENGE_RECORD_TYPE_IN_TIME
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||||
|
|
||||||
|
public enum ChallengeType {
|
||||||
|
CHALLENGE_NONE, //00
|
||||||
|
CHALLENGE_KILL_COUNT, //01
|
||||||
|
CHALLENGE_KILL_COUNT_IN_TIME, //02
|
||||||
|
CHALLENGE_SURVIVE, //03
|
||||||
|
CHALLENGE_TIME_FLY, //04
|
||||||
|
CHALLENGE_KILL_COUNT_FAST, //05
|
||||||
|
CHALLENGE_KILL_COUNT_FROZEN_LESS, //06
|
||||||
|
CHALLENGE_KILL_MONSTER_IN_TIME, //07
|
||||||
|
CHALLENGE_TRIGGER_IN_TIME, //08
|
||||||
|
CHALLENGE_GUARD_HP, //09
|
||||||
|
CHALLENGE_KILL_COUNT_GUARD_HP, //10
|
||||||
|
CHALLENGE_TRIGGER_IN_TIME_FLY , //11
|
||||||
|
//unknown if position and time match from here on
|
||||||
|
CHALLENGE_TRIGGER2_AVOID_TRIGGER1,
|
||||||
|
CHALLENGE_FATHER_SUCC_IN_TIME,
|
||||||
|
CHALLENGE_MONSTER_DAMAGE_COUNT,
|
||||||
|
CHALLENGE_ELEMENT_REACTION_COUNT,
|
||||||
|
CHALLENGE_FREEZE_ENEMY_IN_TIME,
|
||||||
|
CHALLENGE_CRYSTAL_ELEMENT_REACTION_COUNT,
|
||||||
|
CHALLENGE_SHEILD_ABSORB_DAMAGE_COUNT,
|
||||||
|
CHALLENGE_SWIRL_ELEMENT_REACTION_COUNT,
|
||||||
|
CHALLENGE_DIE_LESS_IN_TIME,
|
||||||
|
CHALLENGE_TRIGGER_COUNT,
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||||
|
|
||||||
|
public enum FatherChallengeProperty {
|
||||||
|
DURATION,
|
||||||
|
CUR_SUCC,
|
||||||
|
CUR_FAIL,
|
||||||
|
SUM_SUCC,
|
||||||
|
SUM_FAIL
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP;
|
||||||
|
|
||||||
|
public class KillAndGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// ActiveChallenge with 1,188,234101003,12,3030,0
|
||||||
|
return challengeType == CHALLENGE_KILL_COUNT_GUARD_HP;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override /*TODO check param4 == monstesToKill*/
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int monstersToKill, int gadgetCFGId, int unused, Scene scene, SceneGroup group) {
|
||||||
|
val realGroup = scene.getScriptManager().getGroupById(groupId);
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(monstersToKill, 0),
|
||||||
|
0, // Limit
|
||||||
|
monstersToKill, // Goal
|
||||||
|
List.of(new KillMonsterCountTrigger(), new GuardTrigger(gadgetCFGId)));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KillMonsterCountChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// ActiveChallenge with 1, 1, 241033003, 15, 0, 0
|
||||||
|
return challengeType == ChallengeType.CHALLENGE_KILL_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int groupId, int goal, int param5, int param6, Scene scene, SceneGroup group) {
|
||||||
|
val realGroup = scene.getScriptManager().getGroupById(groupId);
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(goal, groupId),
|
||||||
|
0, // Limit
|
||||||
|
goal, // Goal
|
||||||
|
List.of(new KillMonsterCountTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KillMonsterInTimeChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// ActiveChallenge with 180, 72, 240, 133220161, 133220161, 0
|
||||||
|
return challengeType == ChallengeType.CHALLENGE_KILL_MONSTER_IN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCfgId, int param6, Scene scene, SceneGroup group) {
|
||||||
|
val realGroup = scene.getScriptManager().getGroupById(groupId);
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(timeLimit),
|
||||||
|
timeLimit, // Limit
|
||||||
|
0, // Goal
|
||||||
|
List.of(new KillMonsterTrigger(targetCfgId), new InTimeTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterCountTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class KillMonsterTimeChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// ActiveChallenge with 180,180,45,133108061,1,0
|
||||||
|
// ActiveChallenge Fast with 1001, 5, 15, 240004005, 10, 0
|
||||||
|
return challengeType == ChallengeType.CHALLENGE_KILL_COUNT_IN_TIME ||
|
||||||
|
challengeType == ChallengeType.CHALLENGE_KILL_COUNT_FAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int groupId, int targetCount, int param6, Scene scene, SceneGroup group) {
|
||||||
|
val realGroup = scene.getScriptManager().getGroupById(groupId);
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, realGroup,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(targetCount, timeLimit),
|
||||||
|
timeLimit, // Limit
|
||||||
|
targetCount, // Goal
|
||||||
|
List.of(new KillMonsterCountTrigger(), new InTimeTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.ForTimeTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_SURVIVE;
|
||||||
|
|
||||||
|
public class SurviveChallengeFactoryHandler implements ChallengeFactoryHandler {
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// grp 201055005
|
||||||
|
// ActiveChallenge with 100, 56, 60, 0, 0, 0
|
||||||
|
return challengeType == CHALLENGE_SURVIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int timeToSurvive, int unused4, int unused5, int unused6, Scene scene, SceneGroup group) {
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, group,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(timeToSurvive),
|
||||||
|
timeToSurvive, // Limit
|
||||||
|
0, // Goal
|
||||||
|
List.of(new ForTimeTrigger())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.enums.ChallengeType;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.trigger.TriggerGroupTriggerTrigger;
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_TRIGGER_IN_TIME;
|
||||||
|
|
||||||
|
public class TriggerInTimeChallengeFactoryHandler implements ChallengeFactoryHandler {
|
||||||
|
@Override
|
||||||
|
public boolean isThisType(ChallengeType challengeType) {
|
||||||
|
// kill gadgets(explosive barrel) in time
|
||||||
|
// ActiveChallenge with 56,201,20,2,201,4
|
||||||
|
// open chest in time
|
||||||
|
// ActiveChallenge with 666,202,30,7,202,1
|
||||||
|
return challengeType == CHALLENGE_TRIGGER_IN_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldChallenge build(int challengeIndex, int challengeId, int timeLimit, int param4, int triggerTag, int triggerCount, Scene scene, SceneGroup group) {
|
||||||
|
return new WorldChallenge(
|
||||||
|
scene, group,
|
||||||
|
challengeId, // Id
|
||||||
|
challengeIndex, // Index
|
||||||
|
List.of(timeLimit, triggerCount),
|
||||||
|
timeLimit, // Limit
|
||||||
|
triggerCount, // Goal
|
||||||
|
List.of(new InTimeTrigger(), new TriggerGroupTriggerTrigger(Integer.toString(triggerTag)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
|
||||||
|
public class ForTimeTrigger extends ChallengeTrigger{
|
||||||
|
@Override
|
||||||
|
public void onCheckTimeout(WorldChallenge challenge) {
|
||||||
|
var current = challenge.getScene().getSceneTimeSeconds();
|
||||||
|
if(current - challenge.getStartedAt() > challenge.getTimeLimit()){
|
||||||
|
challenge.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
|
||||||
|
public class KillMonsterCountTrigger extends ChallengeTrigger{
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
|
||||||
|
var newScore = challenge.increaseScore();
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
|
||||||
|
|
||||||
|
if(newScore >= challenge.getGoal()){
|
||||||
|
challenge.done();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.scripts.data.SceneTrigger;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TriggerGroupTriggerTrigger extends ChallengeTrigger{
|
||||||
|
String triggerTag;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGroupTrigger(WorldChallenge challenge, SceneTrigger trigger) {
|
||||||
|
if(!triggerTag.equals(trigger.getTag())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newScore = challenge.increaseScore();
|
||||||
|
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore));
|
||||||
|
if(newScore >= challenge.getGoal()){
|
||||||
|
challenge.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonEndStats;
|
||||||
|
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass.DungeonSettleNotify;
|
||||||
|
import emu.grasscutter.net.proto.ParamListOuterClass;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class BaseDungeonResult {
|
||||||
|
@Getter DungeonData dungeonData;
|
||||||
|
@Getter
|
||||||
|
DungeonEndStats dungeonStats;
|
||||||
|
|
||||||
|
public BaseDungeonResult(DungeonData dungeonData, DungeonEndStats dungeonStats){
|
||||||
|
this.dungeonData = dungeonData;
|
||||||
|
this.dungeonStats = dungeonStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onProto(DungeonSettleNotify.Builder builder){ }
|
||||||
|
|
||||||
|
public final DungeonSettleNotify.Builder getProto(){
|
||||||
|
var success = dungeonStats.getDungeonResult().isSuccess();
|
||||||
|
var builder = DungeonSettleNotify.newBuilder()
|
||||||
|
.setDungeonId(dungeonData.getId())
|
||||||
|
.setIsSuccess(success)
|
||||||
|
.setCloseTime(getCloseTime())
|
||||||
|
.setResult(success ? 1 : 0);
|
||||||
|
|
||||||
|
// TODO check
|
||||||
|
if(dungeonData.getSettleShows()!=null) {
|
||||||
|
for (int i = 0; i < dungeonData.getSettleShows().size(); i++) {
|
||||||
|
var settle = dungeonData.getSettleShows().get(i);
|
||||||
|
builder.putSettleShow(i + 1,switch (settle) {
|
||||||
|
case SETTLE_SHOW_TIME_COST -> ParamListOuterClass.ParamList.newBuilder()
|
||||||
|
.addParamList(settle.getId())
|
||||||
|
.addParamList(dungeonStats.getTimeTaken())
|
||||||
|
.build();
|
||||||
|
case SETTLE_SHOW_KILL_MONSTER_COUNT -> ParamListOuterClass.ParamList.newBuilder()
|
||||||
|
.addParamList(settle.getId())
|
||||||
|
.addParamList(dungeonStats.getKilledMonsters())
|
||||||
|
.build();
|
||||||
|
default -> ParamListOuterClass.ParamList.newBuilder()
|
||||||
|
.addParamList(settle.getId())
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO handle settle show
|
||||||
|
|
||||||
|
onProto(builder);
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCloseTime(){
|
||||||
|
return Utils.getCurrentSeconds() + switch (dungeonStats.getDungeonResult()){
|
||||||
|
case COMPLETED -> dungeonData.getSettleCountdownTime();
|
||||||
|
case FAILED -> dungeonData.getFailSettleCountdownTime();
|
||||||
|
case QUIT -> dungeonData.getQuitSettleCountdownTime();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum DungeonEndReason{
|
||||||
|
COMPLETED,
|
||||||
|
FAILED,
|
||||||
|
QUIT;
|
||||||
|
|
||||||
|
public boolean isSuccess(){
|
||||||
|
return this == COMPLETED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonEndStats;
|
||||||
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.game.tower.TowerManager;
|
||||||
|
import emu.grasscutter.net.proto.DungeonSettleExhibitionInfoOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.ItemParamOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify.ContinueStateType;
|
||||||
|
import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify;
|
||||||
|
|
||||||
|
public class TowerResult extends BaseDungeonResult{
|
||||||
|
WorldChallenge challenge;
|
||||||
|
boolean canJump;
|
||||||
|
boolean hasNextLevel;
|
||||||
|
int nextFloorId;
|
||||||
|
public TowerResult(DungeonData dungeonData, DungeonEndStats dungeonStats, TowerManager towerManager, WorldChallenge challenge) {
|
||||||
|
super(dungeonData, dungeonStats);
|
||||||
|
this.challenge = challenge;
|
||||||
|
this.canJump = towerManager.hasNextFloor();
|
||||||
|
this.hasNextLevel = towerManager.hasNextLevel();
|
||||||
|
this.nextFloorId = hasNextLevel ? 0 : towerManager.getNextFloorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProto(DungeonSettleNotifyOuterClass.DungeonSettleNotify.Builder builder) {
|
||||||
|
var continueStatus = ContinueStateType.CONTINUE_STATE_TYPE_CAN_NOT_CONTINUE_VALUE;
|
||||||
|
if(challenge.isSuccess() && canJump){
|
||||||
|
continueStatus = hasNextLevel ? ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_LEVEL_VALUE
|
||||||
|
: ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_FLOOR_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
var towerLevelEndNotify = TowerLevelEndNotify.newBuilder()
|
||||||
|
.setIsSuccess(challenge.isSuccess())
|
||||||
|
.setContinueState(continueStatus)
|
||||||
|
.addFinishedStarCondList(1)
|
||||||
|
.addFinishedStarCondList(2)
|
||||||
|
.addFinishedStarCondList(3)
|
||||||
|
.addRewardItemList(ItemParamOuterClass.ItemParam.newBuilder()
|
||||||
|
.setItemId(201)
|
||||||
|
.setCount(1000)
|
||||||
|
.build())
|
||||||
|
;
|
||||||
|
if(nextFloorId > 0 && canJump){
|
||||||
|
towerLevelEndNotify.setNextFloorId(nextFloorId);
|
||||||
|
}
|
||||||
|
builder.setTowerLevelEndNotify(towerLevelEndNotify);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonEndStats;
|
||||||
|
import emu.grasscutter.net.proto.DungeonSettleNotifyOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.TrialAvatarFirstPassDungeonNotifyOuterClass.TrialAvatarFirstPassDungeonNotify;
|
||||||
|
|
||||||
|
public class TrialAvatarDungeonResult extends BaseDungeonResult {
|
||||||
|
int trialCharacterIndexId;
|
||||||
|
|
||||||
|
public TrialAvatarDungeonResult(DungeonData dungeonData, DungeonEndStats dungeonStats, int trialCharacterIndexId) {
|
||||||
|
super(dungeonData, dungeonStats);
|
||||||
|
this.trialCharacterIndexId = trialCharacterIndexId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onProto(DungeonSettleNotifyOuterClass.DungeonSettleNotify.Builder builder) {
|
||||||
|
if (dungeonStats.getDungeonResult() == DungeonEndReason.COMPLETED) { //TODO check if its the first pass(?)
|
||||||
|
builder.setTrialAvatarFirstPassDungeonNotify(TrialAvatarFirstPassDungeonNotify.newBuilder()
|
||||||
|
.setTrialAvatarIndexId(trialCharacterIndexId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
public enum DungeonEntrySatisfiedConditionType {
|
||||||
|
DUNGEON_ENTRY_CONDITION_NONE,
|
||||||
|
DUNGEON_ENTRY_CONDITION_LEVEL,
|
||||||
|
DUNGEON_ENTRY_CONDITION_QUEST
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
public enum DungeonInvolveType {
|
||||||
|
INVOLVE_NONE,
|
||||||
|
INVOLVE_ONLY_SINGLE,
|
||||||
|
INVOLVE_SINGLE_MULTIPLE
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
import emu.grasscutter.scripts.constants.IntValueEnum;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum DungeonPassConditionType implements IntValueEnum {
|
||||||
|
DUNGEON_COND_NONE(0),
|
||||||
|
DUNGEON_COND_KILL_MONSTER(3),
|
||||||
|
DUNGEON_COND_KILL_GROUP_MONSTER(5),
|
||||||
|
DUNGEON_COND_KILL_TYPE_MONSTER(7),
|
||||||
|
DUNGEON_COND_FINISH_QUEST(9),
|
||||||
|
DUNGEON_COND_KILL_MONSTER_COUNT(11), // TODO handle count
|
||||||
|
DUNGEON_COND_IN_TIME(13), // Missing triggers and tracking
|
||||||
|
DUNGEON_COND_FINISH_CHALLENGE(14),
|
||||||
|
DUNGEON_COND_END_MULTISTAGE_PLAY(15) // Missing
|
||||||
|
;
|
||||||
|
|
||||||
|
@Getter private final int id;
|
||||||
|
DungeonPassConditionType(int id){
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getValue() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
public enum DungeonPlayType {
|
||||||
|
DUNGEON_PLAY_TYPE_NONE,
|
||||||
|
DUNGEON_PLAY_TYPE_FOGGY_MAZE,
|
||||||
|
DUNGEON_PLAY_TYPE_MIST_TRIAL,
|
||||||
|
DUNGEON_PLAY_TYPE_TRIAL_AVATAR
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
public enum DungeonSubType {
|
||||||
|
DUNGEON_SUB_NONE,
|
||||||
|
DUNGEON_SUB_BOSS,
|
||||||
|
DUNGEON_SUB_TALENT,
|
||||||
|
DUNGEON_SUB_WEAPON,
|
||||||
|
DUNGEON_SUB_RELIQUARY
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum DungeonType {
|
||||||
|
DUNGEON_NONE(false),
|
||||||
|
DUNGEON_PLOT(true),
|
||||||
|
DUNGEON_FIGHT(true),
|
||||||
|
DUNGEON_DAILY_FIGHT(false),
|
||||||
|
DUNGEON_WEEKLY_FIGHT(true),
|
||||||
|
DUNGEON_DISCARDED(false),
|
||||||
|
DUNGEON_TOWER(false),
|
||||||
|
DUNGEON_BOSS(true),
|
||||||
|
DUNGEON_ACTIVITY(false),
|
||||||
|
DUNGEON_EFFIGY(false),
|
||||||
|
DUNGEON_ELEMENT_CHALLENGE(true),
|
||||||
|
DUNGEON_THEATRE_MECHANICUS(false),
|
||||||
|
DUNGEON_FLEUR_FAIR(false),
|
||||||
|
DUNGEON_CHANNELLER_SLAB_LOOP(false),
|
||||||
|
DUNGEON_CHANNELLER_SLAB_ONE_OFF(false),
|
||||||
|
DUNGEON_BLITZ_RUSH(true),
|
||||||
|
DUNGEON_CHESS(false),
|
||||||
|
DUNGEON_SUMO_COMBAT(false),
|
||||||
|
DUNGEON_ROGUELIKE(false),
|
||||||
|
DUNGEON_HACHI(false),
|
||||||
|
DUNGEON_POTION(false),
|
||||||
|
DUNGEON_MINI_ELDRITCH(false),
|
||||||
|
DUNGEON_UGC(false),
|
||||||
|
DUNGEON_GCG(false),
|
||||||
|
DUNGEON_CRYSTAL_LINK(false),
|
||||||
|
DUNGEON_IRODORI_CHESS(false),
|
||||||
|
DUNGEON_ROGUE_DIARY(false),
|
||||||
|
DUNGEON_DREAMLAND(false),
|
||||||
|
DUNGEON_SUMMER_V2(true),
|
||||||
|
DUNGEON_MUQADAS_POTION(false),
|
||||||
|
DUNGEON_INSTABLE_SPRAY(false),
|
||||||
|
DUNGEON_WIND_FIELD(false),
|
||||||
|
DUNGEON_BIGWORLD_MIRROR(false),
|
||||||
|
DUNGEON_FUNGUS_FIGHTER_TRAINING(false),
|
||||||
|
DUNGEON_FUNGUS_FIGHTER_PLOT(false),
|
||||||
|
DUNGEON_EFFIGY_CHALLENGE_V2(false),
|
||||||
|
DUNGEON_CHAR_AMUSEMENT(false);
|
||||||
|
|
||||||
|
@Getter private final boolean countsToBattlepass;
|
||||||
|
|
||||||
|
DungeonType(boolean countsToBattlepass){
|
||||||
|
this.countsToBattlepass = countsToBattlepass;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
public enum DungunEntryType {
|
||||||
|
DUNGEN_ENTRY_TYPE_NONE ,
|
||||||
|
DUNGEN_ENTRY_TYPE_AVATAR_EXP ,
|
||||||
|
DUNGEN_ENTRY_TYPE_WEAPON_PROMOTE,
|
||||||
|
DUNGEN_ENTRY_TYPE_AVATAR_TALENT ,
|
||||||
|
DUNGEN_ENTRY_TYPE_RELIQUARY ,
|
||||||
|
DUNGEN_ENTRY_TYPE_SCOIN ,
|
||||||
|
DUNGEON_ENTRY_TYPE_OBSCURAE ,
|
||||||
|
DUNGEON_ENTRY_TYPE_NORMAL
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.enums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum SettleShowType {
|
||||||
|
SETTLE_SHOW_NONE(0),
|
||||||
|
SETTLE_SHOW_TIME_COST(1),
|
||||||
|
SETTLE_SHOW_OPEN_CHEST_COUNT(2),
|
||||||
|
SETTLE_SHOW_KILL_MONSTER_COUNT(3),
|
||||||
|
SETTLE_SHOW_BLACKSCREEN(4);
|
||||||
|
|
||||||
|
@Getter private final int id;
|
||||||
|
|
||||||
|
SettleShowType(int id){
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.handlers;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
|
||||||
|
public abstract class DungeonBaseHandler {
|
||||||
|
|
||||||
|
public abstract boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_NONE)
|
||||||
|
public class BaseCondition extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE)
|
||||||
|
public class ConditionFinishChallenge extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] == condition.getParam()[0] || params[1] == condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST)
|
||||||
|
public class ConditionFinishQuest extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] == condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_IN_TIME)
|
||||||
|
public class ConditionInTime extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] <= condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER)
|
||||||
|
public class ConditionKillGroupMonster extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] == condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER)
|
||||||
|
public class ConditionKillMonster extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] == condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER_COUNT)
|
||||||
|
public class ConditionKillMonsterCount extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] >= condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package emu.grasscutter.game.dungeons.pass_condition;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.DungeonPassConfigData;
|
||||||
|
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||||
|
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||||
|
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
|
||||||
|
|
||||||
|
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER)
|
||||||
|
public class ConditionKillTypeMonster extends DungeonBaseHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
|
||||||
|
return params[0] == condition.getParam()[0];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.entity.EntityClientGadget;
|
||||||
|
import emu.grasscutter.game.entity.EntityGadget;
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.net.proto.AbilityGadgetInfoOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
|
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
public class GadgetAbility extends GadgetContent {
|
||||||
|
private EntityClientGadget parent;
|
||||||
|
|
||||||
|
public GadgetAbility(EntityGadget gadget, EntityClientGadget parent) {
|
||||||
|
super(gadget);
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onInteract(Player player, GadgetInteractReq req) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||||
|
if (this.parent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val abilityGadgetInfo = AbilityGadgetInfoOuterClass.AbilityGadgetInfo.newBuilder()
|
||||||
|
.setCampId(parent.getCampId())
|
||||||
|
.setCampTargetType(parent.getCampType())
|
||||||
|
.setTargetEntityId(parent.getId())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
gadgetInfo.setAbilityGadget(abilityGadgetInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.platform;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion;
|
||||||
|
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO mostly hardcoded for EntitySolarIsotomaElevatorPlatform, should be more generic
|
||||||
|
*/
|
||||||
|
public class AbilityRoute extends BaseRoute {
|
||||||
|
|
||||||
|
private final Position basePosition;
|
||||||
|
|
||||||
|
public AbilityRoute(Position startRot, boolean startRoute, boolean isActive, Position basePosition) {
|
||||||
|
super(startRot, startRoute, isActive);
|
||||||
|
this.basePosition = basePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
|
||||||
|
return super.toProto()
|
||||||
|
.setStartRot(MathQuaternion.newBuilder().setW(1.0F))
|
||||||
|
.setPosOffset(basePosition.toProto())
|
||||||
|
.setRotOffset(MathQuaternion.newBuilder().setW(1.0F))
|
||||||
|
.setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ABILITY);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.platform;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.MathQuaternionOuterClass.MathQuaternion;
|
||||||
|
import emu.grasscutter.net.proto.PlatformInfoOuterClass.PlatformInfo;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGadget;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
public abstract class BaseRoute {
|
||||||
|
@Getter @Setter private boolean isStarted;
|
||||||
|
@Getter @Setter private boolean isActive;
|
||||||
|
@Getter @Setter private Position startRot;
|
||||||
|
@Getter @Setter private int startSceneTime;
|
||||||
|
@Getter @Setter private int stopSceneTime;
|
||||||
|
|
||||||
|
BaseRoute(Position startRot, boolean isStarted, boolean isActive) {
|
||||||
|
this.startRot = startRot;
|
||||||
|
this.isStarted = isStarted;
|
||||||
|
this.isActive = isActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseRoute(SceneGadget gadget) {
|
||||||
|
this.startRot = gadget.rot;
|
||||||
|
this.isStarted = gadget.start_route;
|
||||||
|
this.isActive = gadget.start_route;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseRoute fromSceneGadget(SceneGadget sceneGadget) {
|
||||||
|
if (sceneGadget.route_id != 0) {
|
||||||
|
return new ConfigRoute(sceneGadget);
|
||||||
|
} else if (sceneGadget.is_use_point_array) {
|
||||||
|
return new PointArrayRoute(sceneGadget);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean startRoute(Scene scene) {
|
||||||
|
if (this.isStarted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.isStarted = true;
|
||||||
|
this.isActive = true;
|
||||||
|
this.startSceneTime = scene.getSceneTime()+300;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean stopRoute(Scene scene) {
|
||||||
|
if (!this.isStarted) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.isStarted = false;
|
||||||
|
this.isActive = false;
|
||||||
|
this.startSceneTime = scene.getSceneTime();
|
||||||
|
this.stopSceneTime = scene.getSceneTime();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MathQuaternion.Builder rotAsMathQuaternion() {
|
||||||
|
val result = MathQuaternion.newBuilder();
|
||||||
|
if (startRot != null) {
|
||||||
|
result.setX(startRot.getX())
|
||||||
|
.setY(startRot.getY())
|
||||||
|
.setZ(startRot.getZ());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlatformInfo.Builder toProto() {
|
||||||
|
val result = PlatformInfo.newBuilder()
|
||||||
|
.setIsStarted(isStarted)
|
||||||
|
.setIsActive(isActive)
|
||||||
|
.setStartRot(rotAsMathQuaternion())
|
||||||
|
.setStartSceneTime(startSceneTime);
|
||||||
|
if (!isStarted) {
|
||||||
|
result.setStopSceneTime(stopSceneTime);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package emu.grasscutter.game.entity.gadget.platform;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
|
||||||
|
import emu.grasscutter.net.proto.PlatformInfoOuterClass;
|
||||||
|
import emu.grasscutter.scripts.data.SceneGadget;
|
||||||
|
import emu.grasscutter.utils.Position;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
public class ConfigRoute extends BaseRoute {
|
||||||
|
|
||||||
|
@Getter @Setter private int routeId;
|
||||||
|
|
||||||
|
public ConfigRoute(SceneGadget gadget) {
|
||||||
|
super(gadget);
|
||||||
|
this.routeId = gadget.route_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigRoute(Position startRot, boolean startRoute, boolean isActive, int routeId) {
|
||||||
|
super(startRot, startRoute, isActive);
|
||||||
|
this.routeId = routeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
|
||||||
|
return super.toProto()
|
||||||
|
.setRouteId(routeId)
|
||||||
|
.setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_USE_CONFIG);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user