mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2026-04-02 14:02:44 +02:00
Copy some files from Grasscutter-Quests
NOT completely finished, nor is it completely done. Protocol issues remain! (including lack of packet IDs)
This commit is contained in:
@@ -9,6 +9,7 @@ import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.entity.gadget.GadgetGatherObject;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
|
||||
@@ -39,6 +40,7 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE -> this.handleModifierChange(invoke);
|
||||
case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA -> this.handleMixinCostStamina(invoke);
|
||||
case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL -> this.handleGenerateElemBall(invoke);
|
||||
case ABILITY_INVOKE_ARGUMENT_META_GLOBAL_FLOAT_VALUE -> this.handleGlobalFloatValue(invoke);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +204,22 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
this.player.getEnergyManager().handleGenerateElemBall(invoke);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a float value ability entry.
|
||||
*
|
||||
* @param invoke The ability invoke entry.
|
||||
*/
|
||||
private void handleGlobalFloatValue(AbilityInvokeEntry invoke)
|
||||
throws InvalidProtocolBufferException {
|
||||
var entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData());
|
||||
if (entry.getKey().hasStr()
|
||||
&& entry.hasFloatValue()
|
||||
&& entry.getFloatValue() == 2.0f
|
||||
&& entry.getKey().getStr().equals("_ABILITY_UziExplode_Count")) {
|
||||
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_SKILL, 10006);
|
||||
}
|
||||
}
|
||||
|
||||
private void invokeAction(
|
||||
AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AchievementData;
|
||||
import emu.grasscutter.data.excels.achievement.AchievementData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
@@ -14,7 +14,19 @@ public class ActivityConfigItem {
|
||||
int scheduleId;
|
||||
List<Integer> meetCondList;
|
||||
Date beginTime;
|
||||
Date openTime;
|
||||
Date closeTime;
|
||||
Date endTime;
|
||||
|
||||
transient ActivityHandler activityHandler;
|
||||
|
||||
void onLoad() {
|
||||
if (openTime == null) {
|
||||
this.openTime = beginTime;
|
||||
}
|
||||
|
||||
if (closeTime == null) {
|
||||
this.closeTime = endTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@ package emu.grasscutter.game.activity;
|
||||
|
||||
import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.ActivityData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityData;
|
||||
import emu.grasscutter.data.server.ActivityCondGroup;
|
||||
import emu.grasscutter.game.activity.condition.ActivityConditionExecutor;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
import emu.grasscutter.utils.DateHelper;
|
||||
import java.util.*;
|
||||
@@ -19,9 +22,9 @@ import lombok.experimental.FieldDefaults;
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public abstract class ActivityHandler {
|
||||
/** Must set before initWatchers */
|
||||
ActivityConfigItem activityConfigItem;
|
||||
@Getter ActivityConfigItem activityConfigItem;
|
||||
|
||||
ActivityData activityData;
|
||||
@Getter ActivityData activityData;
|
||||
Map<WatcherTriggerType, List<ActivityWatcher>> watchersMap = new HashMap<>();
|
||||
|
||||
public abstract void onProtoBuild(
|
||||
@@ -57,6 +60,43 @@ public abstract class ActivityHandler {
|
||||
});
|
||||
}
|
||||
|
||||
protected void triggerCondEvents(Player player) {
|
||||
if (activityData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var questManager = player.getQuestManager();
|
||||
activityData
|
||||
.getCondGroupId()
|
||||
.forEach(
|
||||
condGroupId -> {
|
||||
var condGroup = GameData.getActivityCondGroupMap().get((int) condGroupId);
|
||||
condGroup
|
||||
.getCondIds()
|
||||
.forEach(
|
||||
condition ->
|
||||
questManager.queueEvent(QuestCond.QUEST_COND_ACTIVITY_COND, condition));
|
||||
});
|
||||
}
|
||||
|
||||
private List<Integer> getActivityConditions() {
|
||||
if (activityData == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
return activityData.getCondGroupId().stream()
|
||||
.map(condGroupId -> GameData.getActivityCondGroupMap().get((int) condGroupId))
|
||||
.filter(Objects::nonNull)
|
||||
.map(ActivityCondGroup::getCondIds)
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
}
|
||||
|
||||
// TODO handle possible overwrites
|
||||
private List<Integer> getMeetConditions(ActivityConditionExecutor conditionExecutor) {
|
||||
return conditionExecutor.getMeetActivitiesConditions(getActivityConditions());
|
||||
}
|
||||
|
||||
private Map<Integer, PlayerActivityData.WatcherInfo> initWatchersDataForPlayer() {
|
||||
return watchersMap.values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
@@ -76,7 +116,8 @@ public abstract class ActivityHandler {
|
||||
return playerActivityData;
|
||||
}
|
||||
|
||||
public ActivityInfoOuterClass.ActivityInfo toProto(PlayerActivityData playerActivityData) {
|
||||
public ActivityInfoOuterClass.ActivityInfo toProto(
|
||||
PlayerActivityData playerActivityData, ActivityConditionExecutor conditionExecutor) {
|
||||
var proto = ActivityInfoOuterClass.ActivityInfo.newBuilder();
|
||||
proto
|
||||
.setActivityId(activityConfigItem.getActivityId())
|
||||
@@ -85,7 +126,7 @@ public abstract class ActivityHandler {
|
||||
.setBeginTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
|
||||
.setFirstDayStartTime(DateHelper.getUnixTime(activityConfigItem.getBeginTime()))
|
||||
.setEndTime(DateHelper.getUnixTime(activityConfigItem.getEndTime()))
|
||||
.addAllMeetCondList(activityConfigItem.getMeetCondList());
|
||||
.addAllMeetCondList(getMeetConditions(conditionExecutor));
|
||||
|
||||
if (playerActivityData != null) {
|
||||
proto.addAllWatcherInfoList(playerActivityData.getAllWatcherInfoList());
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.esotericsoftware.reflectasm.ConstructorAccess;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.activity.condition.*;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
@@ -12,6 +13,7 @@ import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Getter;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
@@ -19,6 +21,8 @@ import org.reflections.Reflections;
|
||||
public class ActivityManager extends BasePlayerManager {
|
||||
private static final Map<Integer, ActivityConfigItem> activityConfigItemMap;
|
||||
@Getter private static final Map<Integer, ActivityConfigItem> scheduleActivityConfigMap;
|
||||
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
|
||||
private final ActivityConditionExecutor conditionExecutor;
|
||||
|
||||
static {
|
||||
activityConfigItemMap = new HashMap<>();
|
||||
@@ -26,30 +30,6 @@ public class ActivityManager extends BasePlayerManager {
|
||||
loadActivityConfigData();
|
||||
}
|
||||
|
||||
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
|
||||
|
||||
public ActivityManager(Player player) {
|
||||
super(player);
|
||||
|
||||
playerActivityDataMap = new ConcurrentHashMap<>();
|
||||
// load data for player
|
||||
activityConfigItemMap
|
||||
.values()
|
||||
.forEach(
|
||||
item -> {
|
||||
var data = PlayerActivityData.getByPlayer(player, item.getActivityId());
|
||||
if (data == null) {
|
||||
data = item.getActivityHandler().initPlayerActivityData(player);
|
||||
data.save();
|
||||
}
|
||||
data.setPlayer(player);
|
||||
data.setActivityHandler(item.getActivityHandler());
|
||||
playerActivityDataMap.put(item.getActivityId(), data);
|
||||
});
|
||||
|
||||
player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values()));
|
||||
}
|
||||
|
||||
private static void loadActivityConfigData() {
|
||||
// scan activity type handler & watcher type
|
||||
var activityHandlerTypeMap = new HashMap<ActivityType, ConstructorAccess<?>>();
|
||||
@@ -75,6 +55,7 @@ public class ActivityManager extends BasePlayerManager {
|
||||
DataLoader.loadList("ActivityConfig.json", ActivityConfigItem.class)
|
||||
.forEach(
|
||||
item -> {
|
||||
item.onLoad();
|
||||
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
|
||||
if (activityData == null) {
|
||||
Grasscutter.getLogger().warn("activity {} not exist.", item.getActivityId());
|
||||
@@ -104,6 +85,36 @@ public class ActivityManager extends BasePlayerManager {
|
||||
}
|
||||
}
|
||||
|
||||
public ActivityManager(Player player) {
|
||||
super(player);
|
||||
|
||||
playerActivityDataMap = new ConcurrentHashMap<>();
|
||||
// load data for player
|
||||
activityConfigItemMap
|
||||
.values()
|
||||
.forEach(
|
||||
item -> {
|
||||
var data = PlayerActivityData.getByPlayer(player, item.getActivityId());
|
||||
if (data == null) {
|
||||
data = item.getActivityHandler().initPlayerActivityData(player);
|
||||
data.save();
|
||||
}
|
||||
data.setPlayer(player);
|
||||
data.setActivityHandler(item.getActivityHandler());
|
||||
playerActivityDataMap.put(item.getActivityId(), data);
|
||||
});
|
||||
|
||||
player.sendPacket(new PacketActivityScheduleInfoNotify(activityConfigItemMap.values()));
|
||||
|
||||
conditionExecutor =
|
||||
new BasicActivityConditionExecutor(
|
||||
activityConfigItemMap,
|
||||
GameData.getActivityCondExcelConfigDataMap(),
|
||||
PlayerActivityDataMappingBuilder.buildPlayerActivityDataByActivityCondId(
|
||||
playerActivityDataMap),
|
||||
AllActivityConditionBuilder.buildActivityConditions());
|
||||
}
|
||||
|
||||
/** trigger activity watcher */
|
||||
public void triggerWatcher(WatcherTriggerType watcherTriggerType, String... params) {
|
||||
var watchers =
|
||||
@@ -124,11 +135,71 @@ public class ActivityManager extends BasePlayerManager {
|
||||
params));
|
||||
}
|
||||
|
||||
public boolean isActivityActive(int activityId) {
|
||||
var activityConfig = activityConfigItemMap.get(activityId);
|
||||
if (activityConfig == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
return now.after(activityConfig.getBeginTime()) && now.before(activityConfig.getEndTime());
|
||||
}
|
||||
|
||||
public boolean hasActivityEnded(int activityId) {
|
||||
var activityConfig = activityConfigItemMap.get(activityId);
|
||||
if (activityConfig == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return new Date().after(activityConfig.getEndTime());
|
||||
}
|
||||
|
||||
public boolean isActivityOpen(int activityId) {
|
||||
var activityConfig = activityConfigItemMap.get(activityId);
|
||||
if (activityConfig == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
return now.after(activityConfig.getOpenTime()) && now.before(activityConfig.getCloseTime());
|
||||
}
|
||||
|
||||
public int getOpenDay(int activityId) {
|
||||
var activityConfig = activityConfigItemMap.get(activityId);
|
||||
if (activityConfig == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
return (int)
|
||||
TimeUnit.DAYS.convert(
|
||||
now.getTime() - activityConfig.getOpenTime().getTime(), TimeUnit.MILLISECONDS)
|
||||
+ 1;
|
||||
}
|
||||
|
||||
public boolean isActivityClosed(int activityId) {
|
||||
var activityConfig = activityConfigItemMap.get(activityId);
|
||||
if (activityConfig == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
return now.after(activityConfig.getCloseTime());
|
||||
}
|
||||
|
||||
public boolean meetsCondition(int activityCondId) {
|
||||
return conditionExecutor.meetsCondition(activityCondId);
|
||||
}
|
||||
|
||||
public void triggerActivityConditions() {
|
||||
activityConfigItemMap.forEach((k, v) -> v.getActivityHandler().triggerCondEvents(player));
|
||||
}
|
||||
|
||||
public ActivityInfoOuterClass.ActivityInfo getInfoProtoByActivityId(int activityId) {
|
||||
var activityHandler = activityConfigItemMap.get(activityId).getActivityHandler();
|
||||
var activityData = playerActivityDataMap.get(activityId);
|
||||
|
||||
return activityHandler.toProto(activityData);
|
||||
return activityHandler.toProto(activityData, conditionExecutor);
|
||||
}
|
||||
|
||||
public Optional<ActivityHandler> getActivityHandler(ActivityType type) {
|
||||
@@ -138,7 +209,6 @@ public class ActivityManager extends BasePlayerManager {
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends ActivityHandler> Optional<T> getActivityHandlerAs(
|
||||
ActivityType type, Class<T> clazz) {
|
||||
return getActivityHandler(type).map(x -> (T) x);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.game.activity;
|
||||
|
||||
import emu.grasscutter.data.excels.ActivityWatcherData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -5,7 +5,7 @@ import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ActivityWatcherData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
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();
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link
|
||||
* ActivityCondExcelConfigData}). To use it you should mark class that extends
|
||||
* ActivityConditionBaseHandler, and it will be automatically picked during activity manager
|
||||
* initiation.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ActivityCondition {
|
||||
ActivityConditions value();
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
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);
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import emu.grasscutter.data.excels.activity.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);
|
||||
}
|
||||
|
||||
@@ -1,10 +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);
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ActivityConditionExecutor {
|
||||
|
||||
List<Integer> getMeetActivitiesConditions(List<Integer> condIds);
|
||||
|
||||
boolean meetsCondition(int activityCondId);
|
||||
}
|
||||
|
||||
@@ -1,51 +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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,60 +1,63 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public static 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,75 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.activity.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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,80 +1,81 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.activity.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>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>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>
|
||||
* </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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +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_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
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_LESS;
|
||||
|
||||
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;
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL;
|
||||
|
||||
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;
|
||||
|
||||
@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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +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 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
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_NOT_FINISH_TALK;
|
||||
|
||||
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;
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL;
|
||||
|
||||
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;
|
||||
|
||||
@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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +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 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;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_QUEST_FINISH;
|
||||
|
||||
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;
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.activity.condition.all;
|
||||
|
||||
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER;
|
||||
|
||||
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;
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 java.util.stream.Stream;
|
||||
import lombok.val;
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,144 +1,164 @@
|
||||
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())));
|
||||
}
|
||||
}
|
||||
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.ActivityHandler;
|
||||
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||
import emu.grasscutter.game.activity.DefaultWatcher;
|
||||
import emu.grasscutter.game.activity.GameActivity;
|
||||
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||
import emu.grasscutter.game.dungeons.DungeonTrialTeam;
|
||||
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.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);
|
||||
|
||||
// TODO: Apply trial avatar info.
|
||||
// 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 (this.getSelectedTrialAvatarIndex() <= 0) return;
|
||||
player.getTeamManager().removeTrialAvatar();
|
||||
this.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())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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 java.util.List;
|
||||
import java.util.stream.*;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
import lombok.val;
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,19 @@ import emu.grasscutter.data.binout.OpenConfigEntry;
|
||||
import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import emu.grasscutter.data.excels.*;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens;
|
||||
import emu.grasscutter.data.excels.ItemData.WeaponProperty;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData.InherentProudSkillOpens;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarTalentData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryLevelData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquarySetData;
|
||||
import emu.grasscutter.data.excels.trial.TrialAvatarTemplateData;
|
||||
import emu.grasscutter.data.excels.weapon.WeaponCurveData;
|
||||
import emu.grasscutter.data.excels.weapon.WeaponPromoteData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.inventory.EquipType;
|
||||
@@ -25,11 +36,14 @@ import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData;
|
||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
|
||||
import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip;
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord;
|
||||
import emu.grasscutter.net.proto.TrialAvatarInfoOuterClass.TrialAvatarInfo;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.ProtoHelper;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
@@ -43,7 +57,7 @@ public class Avatar {
|
||||
@Id private ObjectId id;
|
||||
@Indexed @Getter private int ownerId; // Id of player that this avatar belongs to
|
||||
@Transient private Player owner;
|
||||
@Transient @Getter private AvatarData data;
|
||||
@Transient @Getter private AvatarData avatarData;
|
||||
@Transient @Getter private AvatarSkillDepotData skillDepot;
|
||||
@Transient @Getter private long guid; // Player unique id
|
||||
@Getter private int avatarId; // Id of avatar
|
||||
@@ -81,6 +95,17 @@ public class Avatar {
|
||||
@Getter @Setter private int nameCardRewardId;
|
||||
@Getter @Setter private int nameCardId;
|
||||
|
||||
// trial avatar property
|
||||
@Getter @Setter private int trialAvatarId = 0;
|
||||
// cannot store to db if grant reason is not integer
|
||||
@Getter @Setter
|
||||
private int grantReason = TrialAvatarGrantRecord.GrantReason.GRANT_REASON_INVALID.getNumber();
|
||||
|
||||
@Getter @Setter private int fromParentQuestId = 0;
|
||||
// so far no outer class or prop value has information of this, but from packet:
|
||||
// 1 = normal, 2 = trial avatar
|
||||
@Getter @Setter private int avatarType = Type.NORMAL.getNumber();
|
||||
|
||||
@Deprecated // Do not use. Morhpia only!
|
||||
public Avatar() {
|
||||
this.equips = new Int2ObjectOpenHashMap<>();
|
||||
@@ -100,7 +125,7 @@ public class Avatar {
|
||||
this.avatarId = data.getId();
|
||||
this.nameCardRewardId = data.getNameCardRewardId();
|
||||
this.nameCardId = data.getNameCardId();
|
||||
this.data = data;
|
||||
this.avatarData = data;
|
||||
this.bornTime = (int) (System.currentTimeMillis() / 1000);
|
||||
this.flyCloak = 140001;
|
||||
|
||||
@@ -154,16 +179,12 @@ public class Avatar {
|
||||
}
|
||||
|
||||
public ObjectId getObjectId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public AvatarData getAvatarData() {
|
||||
return data;
|
||||
return this.id;
|
||||
}
|
||||
|
||||
protected void setAvatarData(AvatarData data) {
|
||||
if (this.data != null) return;
|
||||
this.data = data; // Used while loading this from the database
|
||||
if (this.avatarData != null) return;
|
||||
this.avatarData = data; // Used while loading this from the database
|
||||
}
|
||||
|
||||
public void setOwner(Player player) {
|
||||
@@ -680,7 +701,7 @@ public class Avatar {
|
||||
this.skillExtraChargeMap.clear();
|
||||
|
||||
// Sanity checks
|
||||
if (this.data == null || this.skillDepot == null) {
|
||||
if (this.avatarData == null || this.skillDepot == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1001,6 +1022,186 @@ public class Avatar {
|
||||
return showAvatarInfo.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this avatar into a trial avatar.
|
||||
*
|
||||
* @param level The avatar's level.
|
||||
* @param avatarId The ID of the avatar.
|
||||
* @param grantReason The reason for granting the avatar.
|
||||
* @param questId The ID of the quest that granted the avatar.
|
||||
*/
|
||||
public void setTrialAvatarInfo(
|
||||
int level, int avatarId, TrialAvatarGrantRecord.GrantReason grantReason, int questId) {
|
||||
this.setLevel(level);
|
||||
this.setPromoteLevel(getMinPromoteLevel(level));
|
||||
this.setTrialAvatarId(avatarId);
|
||||
this.setGrantReason(grantReason.getNumber());
|
||||
this.setFromParentQuestId(questId);
|
||||
this.setAvatarType(Type.TRIAL.getNumber());
|
||||
this.applyTrialSkillLevels();
|
||||
this.applyTrialItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the gear template based on the avatar's level.
|
||||
*
|
||||
* @return The avatar's template.
|
||||
*/
|
||||
private int getTrialTemplate() {
|
||||
return this.getLevel() <= 9
|
||||
? 1
|
||||
: (int)
|
||||
(Math.floor(this.getLevel() / 10f) * 10); // round trial level to fit template levels
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The level to be used for the avatar's skills (talents).
|
||||
*/
|
||||
public int getTrialSkillLevel() {
|
||||
// Use default data if custom data not available.
|
||||
if (GameData.getTrialAvatarCustomData().isEmpty()) {
|
||||
var template = getTrialTemplate(); // round trial level to fit template levels
|
||||
|
||||
var templateData = GameData.getTrialAvatarTemplateDataMap().get(template);
|
||||
return templateData == null ? 1 : templateData.getTrialAvatarSkillLevel();
|
||||
}
|
||||
|
||||
// Use custom data.
|
||||
var trialData = GameData.getTrialAvatarCustomData().get(this.getTrialAvatarId());
|
||||
if (trialData == null) return 1;
|
||||
|
||||
return trialData.getCoreProudSkillLevel(); // enhanced version of weapon
|
||||
}
|
||||
|
||||
/** Applies the correct skill level for the trial avatar. */
|
||||
public void applyTrialSkillLevels() {
|
||||
this.getSkillLevelMap()
|
||||
.keySet()
|
||||
.forEach(skill -> this.setSkillLevel(skill, this.getTrialSkillLevel()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The weapon to use with the avatar.
|
||||
*/
|
||||
public int getTrialWeaponId() {
|
||||
// Use default data if custom data not available.
|
||||
if (GameData.getTrialAvatarCustomData().isEmpty()) {
|
||||
if (GameData.getTrialAvatarDataMap().get(this.getTrialAvatarId()) == null)
|
||||
return this.getAvatarData().getInitialWeapon();
|
||||
|
||||
return GameData.getItemDataMap().get(this.getAvatarData().getInitialWeapon() + 100) == null
|
||||
? getAvatarData().getInitialWeapon()
|
||||
: getAvatarData().getInitialWeapon() + 100; // enhanced version of weapon
|
||||
}
|
||||
|
||||
// Use custom data.
|
||||
var trialData = GameData.getTrialAvatarCustomData().get(this.getTrialAvatarId());
|
||||
if (trialData == null) return 0;
|
||||
|
||||
var trialCustomParams = trialData.getTrialAvatarParamList();
|
||||
return trialCustomParams.size() < 2
|
||||
? getAvatarData().getInitialWeapon()
|
||||
: Integer.parseInt(trialCustomParams.get(1).split(";")[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of artifact IDs to use with the avatar.
|
||||
*/
|
||||
public List<Integer> getTrialReliquary() {
|
||||
// Use default data if custom data not available.
|
||||
if (GameData.getTrialAvatarCustomData().isEmpty()) {
|
||||
int trialAvatarTemplateLevel = getTrialTemplate();
|
||||
|
||||
TrialAvatarTemplateData templateData =
|
||||
GameData.getTrialAvatarTemplateDataMap().get(trialAvatarTemplateLevel);
|
||||
return templateData == null ? List.of() : templateData.getTrialReliquaryList();
|
||||
}
|
||||
|
||||
// Use custom data.
|
||||
var trialData = GameData.getTrialAvatarCustomData().get(this.getTrialAvatarId());
|
||||
if (trialData == null) return List.of();
|
||||
|
||||
var trialCustomParams =
|
||||
GameData.getTrialAvatarCustomData().get(getTrialAvatarId()).getTrialAvatarParamList();
|
||||
return trialCustomParams.size() < 3
|
||||
? List.of()
|
||||
: Stream.of(trialCustomParams.get(2).split(";")).map(Integer::parseInt).toList();
|
||||
}
|
||||
|
||||
/** Applies the correct items for the trial avatar. */
|
||||
public void applyTrialItems() {
|
||||
// Use an enhanced version of the weapon if available.
|
||||
var weapon = new GameItem(this.getTrialWeaponId());
|
||||
weapon.setLevel(this.getLevel());
|
||||
weapon.setExp(0);
|
||||
weapon.setPromoteLevel(getMinPromoteLevel(this.getLevel()));
|
||||
this.getEquips().put(weapon.getEquipSlot(), weapon);
|
||||
|
||||
// Add artifacts for the trial avatar.
|
||||
this.getTrialReliquary()
|
||||
.forEach(
|
||||
id -> {
|
||||
var reliquaryData = GameData.getTrialReliquaryDataMap().get((int) id);
|
||||
if (reliquaryData == null) return;
|
||||
|
||||
var relic = new GameItem(reliquaryData.getReliquaryId());
|
||||
relic.setLevel(reliquaryData.getLevel());
|
||||
relic.setMainPropId(reliquaryData.getMainPropId());
|
||||
relic.getAppendPropIdList().addAll(reliquaryData.getAppendPropList());
|
||||
this.getEquips().put(relic.getEquipSlot(), relic);
|
||||
});
|
||||
|
||||
// Add costume if avatar has a costume.
|
||||
GameData.getAvatarCostumeDataItemIdMap()
|
||||
.values()
|
||||
.forEach(
|
||||
costumeData -> {
|
||||
if (costumeData.getCharacterId() != this.getAvatarId()) return;
|
||||
this.setCostume(costumeData.getId());
|
||||
});
|
||||
}
|
||||
|
||||
/** Equips the items applied from {@link Avatar#applyTrialItems()}. */
|
||||
public void equipTrialItems() {
|
||||
var player = this.getPlayer();
|
||||
|
||||
this.getEquips()
|
||||
.values()
|
||||
.forEach(
|
||||
item -> {
|
||||
item.setEquipCharacter(this.getAvatarId());
|
||||
item.setOwner(player);
|
||||
if (item.getItemData().getEquipType() == EquipType.EQUIP_WEAPON) {
|
||||
item.setWeaponEntityId(player.getWorld().getNextEntityId(EntityIdType.WEAPON));
|
||||
player.sendPacket(new PacketAvatarEquipChangeNotify(this, item));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this (trial) avatar into a trial info protocol buffer.
|
||||
*
|
||||
* @return The trial info protocol buffer.
|
||||
*/
|
||||
public TrialAvatarInfo toTrialInfo() {
|
||||
var trialAvatar =
|
||||
TrialAvatarInfo.newBuilder()
|
||||
.setTrialAvatarId(this.getTrialAvatarId())
|
||||
.setGrantRecord(
|
||||
TrialAvatarGrantRecord.newBuilder()
|
||||
.setGrantReason(this.getGrantReason())
|
||||
.setFromParentQuestId(this.getFromParentQuestId()));
|
||||
|
||||
// Check if the avatar is a trial avatar.
|
||||
if (this.getTrialAvatarId() > 0) {
|
||||
// Add the artifacts & weapons for the avatar.
|
||||
trialAvatar.addAllTrialEquipList(
|
||||
this.getEquips().values().stream().map(GameItem::toProto).toList());
|
||||
}
|
||||
|
||||
return trialAvatar.build();
|
||||
}
|
||||
|
||||
@PostLoad
|
||||
private void onLoad() {}
|
||||
|
||||
@@ -1008,4 +1209,13 @@ public class Avatar {
|
||||
private void prePersist() {
|
||||
this.currentHp = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP);
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
enum Type {
|
||||
NORMAL(0),
|
||||
TRIAL(1);
|
||||
|
||||
final int number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package emu.grasscutter.game.avatar;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
public class BasicDungeonSettleListener implements DungeonSettleListener {
|
||||
|
||||
@Override
|
||||
public void onDungeonSettle(Scene scene) {
|
||||
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
|
||||
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge()));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
public class BasicDungeonSettleListener implements DungeonSettleListener {
|
||||
|
||||
@Override
|
||||
public void onDungeonSettle(Scene scene) {
|
||||
scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000);
|
||||
scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,315 +1,332 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.dungeon.DungeonData;
|
||||
import emu.grasscutter.data.excels.dungeon.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 java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.val;
|
||||
|
||||
/**
|
||||
* 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.getTeamManager().addTrialAvatars(trialTeam.trialAvatarIds);
|
||||
}
|
||||
}
|
||||
|
||||
public void unsetTrialTeam(Player player) {
|
||||
if (this.trialTeam == null) {
|
||||
return;
|
||||
}
|
||||
player.getTeamManager().removeTrialAvatar();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
|
||||
public interface DungeonSettleListener {
|
||||
void onDungeonSettle(Scene scene);
|
||||
}
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
|
||||
public interface DungeonSettleListener {
|
||||
void onDungeonSettle(Scene scene);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,8 @@ package emu.grasscutter.game.dungeons;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.ScenePointEntry;
|
||||
import emu.grasscutter.data.excels.DungeonData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
@@ -18,7 +14,7 @@ import emu.grasscutter.server.packet.send.PacketPlayerEnterDungeonRsp;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonSystem extends BaseGameSystem {
|
||||
public final class DungeonSystem extends BaseGameSystem {
|
||||
private static final BasicDungeonSettleListener basicDungeonSettleObserver =
|
||||
new BasicDungeonSettleListener();
|
||||
|
||||
@@ -27,7 +23,7 @@ public class DungeonSystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
public void getEntryInfo(Player player, int pointId) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
|
||||
var entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId);
|
||||
|
||||
if (entry == null) {
|
||||
// Error
|
||||
@@ -39,24 +35,24 @@ public class DungeonSystem extends BaseGameSystem {
|
||||
}
|
||||
|
||||
public boolean enterDungeon(Player player, int pointId, int dungeonId) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
|
||||
var data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
.debug(
|
||||
"{}({}) is trying to enter dungeon {}",
|
||||
player.getNickname(),
|
||||
player.getUid(),
|
||||
dungeonId);
|
||||
|
||||
int sceneId = data.getSceneId();
|
||||
var sceneId = data.getSceneId();
|
||||
player.getScene().setPrevScene(sceneId);
|
||||
|
||||
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
|
||||
player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver);
|
||||
player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
||||
player.getQuestManager().triggerEvent(QuestContent.QUEST_CONTENT_ENTER_DUNGEON, data.getId());
|
||||
}
|
||||
|
||||
player.getScene().setPrevScenePoint(pointId);
|
||||
@@ -67,13 +63,13 @@ public class DungeonSystem extends BaseGameSystem {
|
||||
/** used in tower dungeons handoff */
|
||||
public boolean handoffDungeon(
|
||||
Player player, int dungeonId, List<DungeonSettleListener> dungeonSettleListeners) {
|
||||
DungeonData data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
|
||||
var data = GameData.getDungeonDataMap().get(dungeonId);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Grasscutter.getLogger()
|
||||
.info(
|
||||
.debug(
|
||||
"{}({}) is trying to enter tower dungeon {}",
|
||||
player.getNickname(),
|
||||
player.getUid(),
|
||||
@@ -82,30 +78,32 @@ public class DungeonSystem extends BaseGameSystem {
|
||||
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
||||
dungeonSettleListeners.forEach(player.getScene()::addDungeonSettleObserver);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void exitDungeon(Player player) {
|
||||
Scene scene = player.getScene();
|
||||
var scene = player.getScene();
|
||||
|
||||
if (scene == null || scene.getSceneType() != SceneType.SCENE_DUNGEON) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get previous scene
|
||||
int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
||||
var prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3;
|
||||
|
||||
// Get previous position
|
||||
DungeonData dungeonData = scene.getDungeonData();
|
||||
Position prevPos = new Position(GameConstants.START_POSITION);
|
||||
var dungeonData = scene.getDungeonData();
|
||||
var prevPos = new Position(GameConstants.START_POSITION);
|
||||
|
||||
if (dungeonData != null) {
|
||||
ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
||||
var entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint());
|
||||
|
||||
if (entry != null) {
|
||||
prevPos.set(entry.getPointData().getTranPos());
|
||||
}
|
||||
}
|
||||
|
||||
// clean temp team if it has
|
||||
player.getTeamManager().cleanTemporaryTeam();
|
||||
player.getTowerManager().clearEntry();
|
||||
@@ -114,9 +112,4 @@ public class DungeonSystem extends BaseGameSystem {
|
||||
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
||||
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
|
||||
}
|
||||
|
||||
public void updateDailyDungeons() {
|
||||
GameData.getScenePointEntries()
|
||||
.forEach((id, entry) -> entry.getPointData().updateDailyDungeon());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
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;
|
||||
}
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class DungeonTrialTeam {
|
||||
List<Integer> trialAvatarIds;
|
||||
TrialAvatarGrantRecord.GrantReason grantReason;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -1,213 +1,55 @@
|
||||
package emu.grasscutter.game.dungeons.challenge;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.DungeonData;
|
||||
import emu.grasscutter.game.dungeons.DungeonDrop;
|
||||
import emu.grasscutter.game.dungeons.DungeonDropEntry;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.net.proto.ResinCostTypeOuterClass;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class DungeonChallenge extends WorldChallenge {
|
||||
|
||||
private static final Int2ObjectMap<List<DungeonDropEntry>> dungeonDropData =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
/** has more challenge */
|
||||
private boolean stage;
|
||||
|
||||
private IntSet rewardedPlayers;
|
||||
|
||||
public DungeonChallenge(
|
||||
Scene scene,
|
||||
SceneGroup group,
|
||||
int challengeId,
|
||||
int challengeIndex,
|
||||
List<Integer> paramList,
|
||||
int timeLimit,
|
||||
int goal,
|
||||
List<ChallengeTrigger> challengeTriggers) {
|
||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||
this.setRewardedPlayers(new IntOpenHashSet());
|
||||
}
|
||||
|
||||
public static void initialize() {
|
||||
// Read the data we need for dungeon rewards drops.
|
||||
try {
|
||||
DataLoader.loadList("DungeonDrop.json", DungeonDrop.class)
|
||||
.forEach(
|
||||
entry -> {
|
||||
dungeonDropData.put(entry.getDungeonId(), entry.getDrops());
|
||||
});
|
||||
Grasscutter.getLogger().debug("Loaded {} dungeon drop data entries.", dungeonDropData.size());
|
||||
} catch (Exception ex) {
|
||||
Grasscutter.getLogger().error("Unable to load dungeon drop data.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public IntSet getRewardedPlayers() {
|
||||
return rewardedPlayers;
|
||||
}
|
||||
|
||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||
this.rewardedPlayers = rewardedPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
super.done();
|
||||
if (this.isSuccess()) {
|
||||
// Settle
|
||||
settle();
|
||||
}
|
||||
}
|
||||
|
||||
private void settle() {
|
||||
if (!stage) {
|
||||
var scene = this.getScene();
|
||||
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(getScene()));
|
||||
scene
|
||||
.getScriptManager()
|
||||
.callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||
// Battle pass trigger
|
||||
scene
|
||||
.getPlayers()
|
||||
.forEach(
|
||||
p ->
|
||||
p.getBattlePassManager()
|
||||
.triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON));
|
||||
}
|
||||
}
|
||||
|
||||
private List<GameItem> rollRewards(boolean useCondensed) {
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
int dungeonId = this.getScene().getDungeonData().getId();
|
||||
// If we have specific drop data for this dungeon, we use it.
|
||||
if (dungeonDropData.containsKey(dungeonId)) {
|
||||
List<DungeonDropEntry> dropEntries = dungeonDropData.get(dungeonId);
|
||||
|
||||
// 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 : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||
DungeonData dungeonData = getScene().getDungeonData();
|
||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||
|
||||
if (!isSuccess()
|
||||
|| dungeonData == null
|
||||
|| dungeonData.getRewardPreview() == null
|
||||
|| dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already rewarded
|
||||
if (getRewardedPlayers().contains(player.getUid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rewards.
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
|
||||
if (request.getResinCostType()
|
||||
== ResinCostTypeOuterClass.ResinCostType.RESIN_COST_TYPE_CONDENSE) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Spend the condensed resin and only proceed if the transaction succeeds.
|
||||
if (!player.getResinManager().useCondensedResin(1)) return;
|
||||
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(true));
|
||||
} else {
|
||||
// Spend the resin and only proceed if the transaction succeeds.
|
||||
if (!player.getResinManager().useResin(resinCost)) return;
|
||||
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards(false));
|
||||
}
|
||||
|
||||
// Add rewards to player and send notification.
|
||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
getRewardedPlayers().add(player.getUid());
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge;
|
||||
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class DungeonChallenge extends WorldChallenge {
|
||||
|
||||
/**
|
||||
* has more challenge
|
||||
*/
|
||||
private boolean stage;
|
||||
|
||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||
int challengeId, int challengeIndex,
|
||||
List<Integer> paramList,
|
||||
int timeLimit, int goal,
|
||||
List<ChallengeTrigger> challengeTriggers) {
|
||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||
}
|
||||
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
super.done();
|
||||
this.getScene().triggerDungeonEvent(
|
||||
DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE,
|
||||
this.getChallengeId(), this.getChallengeIndex());
|
||||
|
||||
if (this.isSuccess())
|
||||
this.settle();
|
||||
}
|
||||
|
||||
private void settle() {
|
||||
if (!stage) {
|
||||
var scene = this.getScene();
|
||||
/*if(this.isSuccess()){
|
||||
scene.getDungeonManager().finishDungeon();
|
||||
} else {
|
||||
scene.getDungeonManager().failDungeon();
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,9 +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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||
|
||||
public enum ChallengeRecordType {
|
||||
CHALLENGE_RECORD_TYPE_NONE,
|
||||
CHALLENGE_RECORD_TYPE_IN_TIME
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||
|
||||
public enum ChallengeRecordType {
|
||||
CHALLENGE_RECORD_TYPE_NONE,
|
||||
CHALLENGE_RECORD_TYPE_IN_TIME
|
||||
}
|
||||
|
||||
@@ -1,27 +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,
|
||||
}
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||
|
||||
public enum FatherChallengeProperty {
|
||||
DURATION,
|
||||
CUR_SUCC,
|
||||
CUR_FAIL,
|
||||
SUM_SUCC,
|
||||
SUM_FAIL
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.enums;
|
||||
|
||||
public enum FatherChallengeProperty {
|
||||
DURATION,
|
||||
CUR_SUCC,
|
||||
CUR_FAIL,
|
||||
SUM_SUCC,
|
||||
SUM_FAIL
|
||||
}
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
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)));
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_KILL_COUNT_GUARD_HP;
|
||||
|
||||
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 java.util.List;
|
||||
import lombok.val;
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
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 java.util.List;
|
||||
import lombok.val;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,40 @@
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
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 java.util.List;
|
||||
import lombok.val;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,42 @@
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
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 java.util.List;
|
||||
import lombok.val;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,40 @@
|
||||
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())
|
||||
);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_SURVIVE;
|
||||
|
||||
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;
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
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)))
|
||||
);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import static emu.grasscutter.game.dungeons.challenge.enums.ChallengeType.CHALLENGE_TRIGGER_IN_TIME;
|
||||
|
||||
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;
|
||||
|
||||
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))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +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();
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +1,77 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,54 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.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.DungeonSettleNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass;
|
||||
import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify;
|
||||
import emu.grasscutter.net.proto.TowerLevelEndNotifyOuterClass.TowerLevelEndNotify.ContinueStateType;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.dungeon_results;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.dungeons.enums;
|
||||
|
||||
public enum DungeonEntrySatisfiedConditionType {
|
||||
DUNGEON_ENTRY_CONDITION_NONE,
|
||||
DUNGEON_ENTRY_CONDITION_LEVEL,
|
||||
DUNGEON_ENTRY_CONDITION_QUEST
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.enums;
|
||||
|
||||
public enum DungeonEntrySatisfiedConditionType {
|
||||
DUNGEON_ENTRY_CONDITION_NONE,
|
||||
DUNGEON_ENTRY_CONDITION_LEVEL,
|
||||
DUNGEON_ENTRY_CONDITION_QUEST
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.dungeons.enums;
|
||||
|
||||
public enum DungeonInvolveType {
|
||||
INVOLVE_NONE,
|
||||
INVOLVE_ONLY_SINGLE,
|
||||
INVOLVE_SINGLE_MULTIPLE
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.enums;
|
||||
|
||||
public enum DungeonInvolveType {
|
||||
INVOLVE_NONE,
|
||||
INVOLVE_ONLY_SINGLE,
|
||||
INVOLVE_SINGLE_MULTIPLE
|
||||
}
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,9 +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
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.enums;
|
||||
|
||||
public enum DungeonSubType {
|
||||
DUNGEON_SUB_NONE,
|
||||
DUNGEON_SUB_BOSS,
|
||||
DUNGEON_SUB_TALENT,
|
||||
DUNGEON_SUB_WEAPON,
|
||||
DUNGEON_SUB_RELIQUARY
|
||||
}
|
||||
|
||||
@@ -1,49 +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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,17 +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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +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);
|
||||
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.handlers;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
|
||||
public abstract class DungeonBaseHandler {
|
||||
|
||||
public abstract boolean execute(
|
||||
DungeonPassConfigData.DungeonPassCondition condition, int... params);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +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];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.pass_condition;
|
||||
|
||||
import emu.grasscutter.data.excels.dungeon.DungeonPassConfigData;
|
||||
import emu.grasscutter.game.dungeons.DungeonValue;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package emu.grasscutter.game.entity;
|
||||
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.inventory.EquipType;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
|
||||
@@ -3,8 +3,8 @@ package emu.grasscutter.game.entity;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.PropGrowCurve;
|
||||
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
|
||||
import emu.grasscutter.data.excels.MonsterCurveData;
|
||||
import emu.grasscutter.data.excels.MonsterData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterCurveData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.*;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
|
||||
@@ -1,39 +1,37 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +1,83 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* TODO implement point array routes, read from missing resources
|
||||
*/
|
||||
public class PointArrayRoute extends BaseRoute {
|
||||
|
||||
@Getter @Setter int currentPoint;
|
||||
@Getter @Setter int pointArrayId;
|
||||
|
||||
public PointArrayRoute(SceneGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public PointArrayRoute(Position startRot, boolean startRoute, boolean isActive, int pointArrayId) {
|
||||
super(startRot, startRoute, isActive);
|
||||
this.pointArrayId = pointArrayId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
|
||||
return super.toProto()
|
||||
.setMovingPlatformType(MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ROUTE);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
/** TODO implement point array routes, read from missing resources */
|
||||
public class PointArrayRoute extends BaseRoute {
|
||||
|
||||
@Getter @Setter int currentPoint;
|
||||
@Getter @Setter int pointArrayId;
|
||||
|
||||
public PointArrayRoute(SceneGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public PointArrayRoute(
|
||||
Position startRot, boolean startRoute, boolean isActive, int pointArrayId) {
|
||||
super(startRot, startRoute, isActive);
|
||||
this.pointArrayId = pointArrayId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
|
||||
return super.toProto()
|
||||
.setMovingPlatformType(
|
||||
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_ROUTE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.ReliquaryMainPropData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package emu.grasscutter.game.managers.blossom;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.MonsterData;
|
||||
import emu.grasscutter.data.excels.WorldLevelData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterData;
|
||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||
|
||||
@@ -6,9 +6,9 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.data.excels.MonsterData.HpDrops;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterData.HpDrops;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.*;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
|
||||
@@ -5,7 +5,7 @@ import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.PlayerLevelData;
|
||||
import emu.grasscutter.data.excels.WeatherData;
|
||||
import emu.grasscutter.data.excels.world.WeatherData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.CoopRequest;
|
||||
@@ -44,6 +44,7 @@ import emu.grasscutter.game.props.ClimateType;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.game.shop.ShopLimit;
|
||||
import emu.grasscutter.game.tower.TowerData;
|
||||
@@ -93,235 +94,122 @@ import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
@Entity(value = "players", useDiscriminator = false)
|
||||
public class Player {
|
||||
private transient final Int2ObjectMap<CoopRequest> coopRequests; // Synchronized getter
|
||||
@Getter
|
||||
private transient final Queue<AttackResult> attackResults;
|
||||
@Getter
|
||||
private transient final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
|
||||
@Getter
|
||||
private transient final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
|
||||
@Getter
|
||||
private transient final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||
@Id
|
||||
private int id;
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
private String accountId;
|
||||
@Setter
|
||||
private transient Account account;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient GameSession session;
|
||||
@Getter
|
||||
private String nickname;
|
||||
@Getter
|
||||
private String signature;
|
||||
@Getter
|
||||
private int headImage;
|
||||
@Getter
|
||||
private int nameCardId = 210001;
|
||||
@Getter
|
||||
private final Position position;
|
||||
@Getter
|
||||
@Setter
|
||||
private Position prevPos;
|
||||
@Getter
|
||||
private final Position rotation;
|
||||
@Getter
|
||||
private PlayerBirthday birthday;
|
||||
@Getter
|
||||
private PlayerCodex codex;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean showAvatars;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> showAvatarList;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> showNameCardList;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> properties;
|
||||
@Getter
|
||||
@Setter
|
||||
private int currentRealmId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int widgetId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int sceneId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int regionId;
|
||||
@Getter
|
||||
private int mainCharacterId;
|
||||
@Setter
|
||||
private boolean godmode; // Getter is inGodmode
|
||||
@Id private int id;
|
||||
@Indexed(options = @IndexOptions(unique = true)) private String accountId;
|
||||
@Setter private transient Account account;
|
||||
@Getter @Setter private transient GameSession session;
|
||||
|
||||
@Getter private String nickname;
|
||||
@Getter private String signature;
|
||||
@Getter private int headImage;
|
||||
@Getter private int nameCardId = 210001;
|
||||
@Getter private Position position;
|
||||
@Getter @Setter private Position prevPos;
|
||||
@Getter private Position rotation;
|
||||
@Getter private PlayerBirthday birthday;
|
||||
@Getter private PlayerCodex codex;
|
||||
@Getter @Setter private boolean showAvatars;
|
||||
@Getter @Setter private List<Integer> showAvatarList;
|
||||
@Getter @Setter private List<Integer> showNameCardList;
|
||||
@Getter private Map<Integer, Integer> properties;
|
||||
@Getter @Setter private int currentRealmId;
|
||||
@Getter @Setter private int widgetId;
|
||||
@Getter @Setter private int sceneId;
|
||||
@Getter @Setter private int regionId;
|
||||
@Getter private int mainCharacterId;
|
||||
@Setter private boolean godmode; // Getter is inGodmode
|
||||
private boolean stamina; // Getter is getUnlimitedStamina, Setter is setUnlimitedStamina
|
||||
@Getter
|
||||
private final Set<Integer> nameCardList;
|
||||
@Getter
|
||||
private final Set<Integer> flyCloakList;
|
||||
@Getter
|
||||
private final Set<Integer> costumeList;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> rewardedLevels;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> homeRewardedLevels;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> realmList;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> seenRealmList;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedForgingBlueprints;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedCombines;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedFurniture;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedFurnitureSuite;
|
||||
@Getter
|
||||
private final Map<Long, ExpeditionInfo> expeditionInfo;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> unlockedRecipies;
|
||||
@Getter
|
||||
private final List<ActiveForgeData> activeForges;
|
||||
@Getter
|
||||
private final Map<Integer, ActiveCookCompoundData> activeCookCompounds;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> questGlobalVariables;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> openStates;
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<Integer, Set<Integer>> unlockedSceneAreas;
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<Integer, Set<Integer>> unlockedScenePoints;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> chatEmojiIdList;
|
||||
@Transient
|
||||
private long nextGuid = 0;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private int peerId;
|
||||
@Transient
|
||||
private World world; // Synchronized getter and setter
|
||||
@Transient
|
||||
private Scene scene; // Synchronized getter and setter
|
||||
@Transient
|
||||
@Getter
|
||||
private int weatherId = 0;
|
||||
@Transient
|
||||
@Getter
|
||||
private ClimateType climate = ClimateType.CLIMATE_SUNNY;
|
||||
|
||||
@Getter private Set<Integer> nameCardList;
|
||||
@Getter private Set<Integer> flyCloakList;
|
||||
@Getter private Set<Integer> costumeList;
|
||||
@Getter @Setter private Set<Integer> rewardedLevels;
|
||||
@Getter @Setter private Set<Integer> homeRewardedLevels;
|
||||
@Getter @Setter private Set<Integer> realmList;
|
||||
@Getter @Setter private Set<Integer> seenRealmList;
|
||||
@Getter private Set<Integer> unlockedForgingBlueprints;
|
||||
@Getter private Set<Integer> unlockedCombines;
|
||||
@Getter private Set<Integer> unlockedFurniture;
|
||||
@Getter private Set<Integer> unlockedFurnitureSuite;
|
||||
@Getter private Map<Long, ExpeditionInfo> expeditionInfo;
|
||||
@Getter private Map<Integer, Integer> unlockedRecipies;
|
||||
@Getter private List<ActiveForgeData> activeForges;
|
||||
@Getter private Map<Integer, ActiveCookCompoundData> activeCookCompounds;
|
||||
@Getter private Map<Integer, Integer> questGlobalVariables;
|
||||
@Getter private Map<Integer, Integer> openStates;
|
||||
@Getter @Setter private Map<Integer, Set<Integer>> unlockedSceneAreas;
|
||||
@Getter @Setter private Map<Integer, Set<Integer>> unlockedScenePoints;
|
||||
@Getter @Setter private List<Integer> chatEmojiIdList;
|
||||
|
||||
@Transient private long nextGuid = 0;
|
||||
@Transient @Getter @Setter private int peerId;
|
||||
@Transient private World world; // Synchronized getter and setter
|
||||
@Transient private Scene scene; // Synchronized getter and setter
|
||||
@Transient @Getter private int weatherId = 0;
|
||||
@Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY;
|
||||
|
||||
// Player managers go here
|
||||
@Getter
|
||||
private final transient AvatarStorage avatars;
|
||||
@Getter
|
||||
private final transient Inventory inventory;
|
||||
@Getter
|
||||
private final transient FriendsList friendsList;
|
||||
@Getter
|
||||
private final transient MailHandler mailHandler;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient MessageHandler messageHandler;
|
||||
@Getter
|
||||
private final transient AbilityManager abilityManager;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient QuestManager questManager;
|
||||
@Getter
|
||||
private final transient TowerManager towerManager;
|
||||
@Getter
|
||||
private transient SotSManager sotsManager;
|
||||
@Getter
|
||||
private transient MapMarksManager mapMarksManager;
|
||||
@Getter
|
||||
private transient StaminaManager staminaManager;
|
||||
@Getter
|
||||
private transient EnergyManager energyManager;
|
||||
@Getter
|
||||
private transient ResinManager resinManager;
|
||||
@Getter
|
||||
private transient ForgingManager forgingManager;
|
||||
@Getter
|
||||
private transient DeforestationManager deforestationManager;
|
||||
@Getter
|
||||
private transient FurnitureManager furnitureManager;
|
||||
@Getter
|
||||
private transient BattlePassManager battlePassManager;
|
||||
@Getter
|
||||
private transient CookingManager cookingManager;
|
||||
@Getter
|
||||
private transient CookingCompoundManager cookingCompoundManager;
|
||||
@Getter
|
||||
private transient ActivityManager activityManager;
|
||||
@Getter
|
||||
private final transient PlayerBuffManager buffManager;
|
||||
@Getter
|
||||
private transient PlayerProgressManager progressManager;
|
||||
@Getter
|
||||
private transient SatiationManager satiationManager;
|
||||
@Getter private transient AvatarStorage avatars;
|
||||
@Getter private transient Inventory inventory;
|
||||
@Getter private transient FriendsList friendsList;
|
||||
@Getter private transient MailHandler mailHandler;
|
||||
@Getter @Setter private transient MessageHandler messageHandler;
|
||||
@Getter private transient AbilityManager abilityManager;
|
||||
@Getter @Setter private transient QuestManager questManager;
|
||||
@Getter private transient TowerManager towerManager;
|
||||
@Getter private transient SotSManager sotsManager;
|
||||
@Getter private transient MapMarksManager mapMarksManager;
|
||||
@Getter private transient StaminaManager staminaManager;
|
||||
@Getter private transient EnergyManager energyManager;
|
||||
@Getter private transient ResinManager resinManager;
|
||||
@Getter private transient ForgingManager forgingManager;
|
||||
@Getter private transient DeforestationManager deforestationManager;
|
||||
@Getter private transient FurnitureManager furnitureManager;
|
||||
@Getter private transient BattlePassManager battlePassManager;
|
||||
@Getter private transient CookingManager cookingManager;
|
||||
@Getter private transient CookingCompoundManager cookingCompoundManager;
|
||||
@Getter private transient ActivityManager activityManager;
|
||||
@Getter private transient PlayerBuffManager buffManager;
|
||||
@Getter private transient PlayerProgressManager progressManager;
|
||||
@Getter private transient SatiationManager satiationManager;
|
||||
|
||||
// Manager data (Save-able to the database)
|
||||
@Getter
|
||||
private transient Achievements achievements;
|
||||
@Getter private transient Achievements achievements;
|
||||
private PlayerProfile playerProfile; // Getter has null-check
|
||||
@Getter
|
||||
private TeamManager teamManager;
|
||||
@Getter private TeamManager teamManager;
|
||||
private TowerData towerData; // Getter has null-check
|
||||
@Getter
|
||||
private final PlayerGachaInfo gachaInfo;
|
||||
@Getter private PlayerGachaInfo gachaInfo;
|
||||
private PlayerCollectionRecords collectionRecordStore; // Getter has null-check
|
||||
@Getter
|
||||
private final ArrayList<ShopLimit> shopLimit;
|
||||
@Getter
|
||||
private transient GameHome home;
|
||||
@Setter
|
||||
private boolean moonCard; // Getter is inMoonCard
|
||||
@Getter
|
||||
@Setter
|
||||
private Date moonCardStartTime;
|
||||
@Getter
|
||||
@Setter
|
||||
private int moonCardDuration;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Date> moonCardGetTimes;
|
||||
@Transient
|
||||
@Getter
|
||||
private boolean paused;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private int enterSceneToken;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private SceneLoadState sceneLoadState = SceneLoadState.NONE;
|
||||
@Transient
|
||||
private boolean hasSentLoginPackets;
|
||||
@Transient
|
||||
private long nextSendPlayerLocTime = 0;
|
||||
@Getter
|
||||
@Setter
|
||||
private long springLastUsed;
|
||||
@Getter private ArrayList<ShopLimit> shopLimit;
|
||||
|
||||
@Getter private transient GameHome home;
|
||||
|
||||
@Setter private boolean moonCard; // Getter is inMoonCard
|
||||
@Getter @Setter private Date moonCardStartTime;
|
||||
@Getter @Setter private int moonCardDuration;
|
||||
@Getter @Setter private Set<Date> moonCardGetTimes;
|
||||
|
||||
@Transient @Getter private boolean paused;
|
||||
@Transient @Getter @Setter private int enterSceneToken;
|
||||
@Transient @Getter @Setter private SceneLoadState sceneLoadState = SceneLoadState.NONE;
|
||||
@Transient private boolean hasSentLoginPackets;
|
||||
@Transient private long nextSendPlayerLocTime = 0;
|
||||
|
||||
private transient final Int2ObjectMap<CoopRequest> coopRequests; // Synchronized getter
|
||||
@Getter private transient final Queue<AttackResult> attackResults;
|
||||
@Getter private transient final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
|
||||
@Getter private transient final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
|
||||
@Getter private transient final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||
|
||||
@Getter @Setter private long springLastUsed;
|
||||
private HashMap<String, MapMark> mapMarks; // Getter makes an empty hashmap - maybe do this elsewhere?
|
||||
@Getter
|
||||
@Setter
|
||||
private int nextResinRefresh;
|
||||
@Getter
|
||||
@Setter
|
||||
private int lastDailyReset;
|
||||
@Getter
|
||||
private final transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TODO
|
||||
@Getter @Setter private int nextResinRefresh;
|
||||
@Getter @Setter private int lastDailyReset;
|
||||
@Getter private transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY;
|
||||
@Getter private long playerGameTime = 0;
|
||||
|
||||
@Getter private PlayerProgress playerProgress;
|
||||
@Getter private Set<Integer> activeQuestTimers;
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||
@@ -432,6 +320,18 @@ public class Player {
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the player's game time if it has changed.
|
||||
*
|
||||
* @param gameTime The new game time.
|
||||
*/
|
||||
public void updatePlayerGameTime(long gameTime) {
|
||||
if (this.playerGameTime == gameTime) return;
|
||||
this.playerGameTime = gameTime;
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
return id;
|
||||
}
|
||||
@@ -717,7 +617,8 @@ public class Player {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
||||
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -730,7 +631,8 @@ public class Player {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("LEAVE_REGION_" + region.config_id, true))) {
|
||||
getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -959,6 +861,11 @@ public class Player {
|
||||
this.sendPacket(new PacketSetNameCardRsp(nameCardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to this player.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public void dropMessage(Object message) {
|
||||
if (this.messageHandler != null) {
|
||||
this.messageHandler.append(message.toString());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.excels.BuffData;
|
||||
@@ -17,13 +16,14 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.Getter;
|
||||
|
||||
public class PlayerBuffManager extends BasePlayerManager {
|
||||
public final class PlayerBuffManager extends BasePlayerManager {
|
||||
private final List<PlayerBuff> pendingBuffs;
|
||||
private final Int2ObjectMap<PlayerBuff> buffs; // Server buffs
|
||||
private int nextBuffUid;
|
||||
|
||||
public PlayerBuffManager(Player player) {
|
||||
super(player);
|
||||
|
||||
this.buffs = new Int2ObjectOpenHashMap<>();
|
||||
this.pendingBuffs = new ArrayList<>();
|
||||
}
|
||||
@@ -92,42 +92,40 @@ public class PlayerBuffManager extends BasePlayerManager {
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration, Avatar target) {
|
||||
// Get buff excel data
|
||||
BuffData buffData = GameData.getBuffDataMap().get(buffId);
|
||||
var buffData = GameData.getBuffDataMap().get(buffId);
|
||||
if (buffData == null) return false;
|
||||
|
||||
boolean success = false;
|
||||
|
||||
// Perform onAdded actions
|
||||
success |=
|
||||
var success =
|
||||
Optional.ofNullable(GameData.getAbilityData(buffData.getAbilityName()))
|
||||
.map(data -> data.modifiers.get(buffData.getModifierName()))
|
||||
.map(modifier -> modifier.onAdded)
|
||||
.map(
|
||||
onAdded -> {
|
||||
var s = false;
|
||||
for (var a : onAdded) {
|
||||
Grasscutter.getLogger().debug("onAdded exists");
|
||||
if (Objects.requireNonNull(a.type) == AbilityModifierAction.Type.HealHP) {
|
||||
Grasscutter.getLogger().debug("Attempting heal");
|
||||
var shouldHeal = false;
|
||||
for (var ability : onAdded) {
|
||||
if (Objects.requireNonNull(ability.type) == AbilityModifierAction.Type.HealHP) {
|
||||
if (target == null) continue;
|
||||
|
||||
var maxHp = target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
var amount = a.amount.get() + a.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
var amount =
|
||||
ability.amount.get() + ability.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
|
||||
target.getAsEntity().heal(amount);
|
||||
s = true;
|
||||
Grasscutter.getLogger().debug("Healed {}", amount);
|
||||
shouldHeal = true;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
return shouldHeal;
|
||||
})
|
||||
.orElse(false);
|
||||
Grasscutter.getLogger().debug("Oh no");
|
||||
|
||||
// Set duration
|
||||
if (duration < 0f) {
|
||||
duration = buffData.getTime();
|
||||
}
|
||||
|
||||
// Dont add buff if duration is equal or less than 0
|
||||
// Don't add buff if duration is equal or less than 0
|
||||
if (duration <= 0) {
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package emu.grasscutter.game.player;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.CodexAnimalData;
|
||||
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
|
||||
@@ -1,63 +1,60 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tracks progress the player made in the world, like obtained items, seen characters and more
|
||||
*/
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||
// it will be hard to loop and compare
|
||||
private Map<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress(){
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId){
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count){
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId){
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count){
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry{
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId){
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount){
|
||||
this.obtainedCount+=amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
/** Tracks progress the player made in the world, like obtained items, seen characters and more */
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||
// it will be hard to loop and compare
|
||||
private Map<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId) {
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count) {
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId) {
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count) {
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry {
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount) {
|
||||
this.obtainedCount += amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import emu.grasscutter.data.excels.OpenStateData;
|
||||
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import java.util.Set;
|
||||
@@ -222,7 +221,7 @@ public class PlayerProgressManager extends BasePlayerDataManager {
|
||||
// Fire quest trigger for trans point unlock.
|
||||
this.player
|
||||
.getQuestManager()
|
||||
.triggerEvent(QuestTrigger.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
||||
.triggerEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
||||
|
||||
// Send packet.
|
||||
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
||||
|
||||
@@ -5,14 +5,17 @@ import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.EntityBaseGadget;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.EnterReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
@@ -20,18 +23,21 @@ import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
||||
import emu.grasscutter.server.event.player.PlayerTeamDeathEvent;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class TeamManager extends BasePlayerDataManager {
|
||||
@@ -45,8 +51,16 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
@Getter @Setter private int currentCharacterIndex;
|
||||
@Transient @Getter @Setter private TeamInfo mpTeam;
|
||||
@Transient @Getter @Setter private int entityId;
|
||||
|
||||
@Transient private int useTemporarilyTeamIndex = -1;
|
||||
@Transient private List<TeamInfo> temporaryTeam; // Temporary Team for tower
|
||||
@Transient @Getter @Setter private boolean usingTrialTeam;
|
||||
@Transient @Getter @Setter private TeamInfo trialAvatarTeam;
|
||||
// hold trial avatars for later use in rebuilding active team
|
||||
@Transient @Getter @Setter private Map<Integer, Avatar> trialAvatars;
|
||||
|
||||
@Transient @Getter @Setter
|
||||
private int previousIndex = -1; // index of character selection in team before adding trial avatar
|
||||
|
||||
public TeamManager() {
|
||||
this.mpTeam = new TeamInfo();
|
||||
@@ -270,6 +284,18 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates all properties of the active team. */
|
||||
public void updateTeamProperties() {
|
||||
this.updateTeamResonances(); // Update team resonances.
|
||||
this.getPlayer()
|
||||
.sendPacket(new PacketSceneTeamUpdateNotify(this.getPlayer())); // Notify the player.
|
||||
|
||||
// Skill charges packet - Yes, this is official server behavior as of 2.6.0
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.forEach(Avatar::sendSkillExtraChargeMap);
|
||||
}
|
||||
|
||||
public void updateTeamEntities(BasePacket responsePacket) {
|
||||
// Sanity check - Should never happen
|
||||
if (this.getCurrentTeamInfo().getAvatars().size() <= 0) {
|
||||
@@ -277,9 +303,9 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
|
||||
// If current team has changed
|
||||
EntityAvatar currentEntity = this.getCurrentAvatarEntity();
|
||||
Int2ObjectMap<EntityAvatar> existingAvatars = new Int2ObjectOpenHashMap<>();
|
||||
int prevSelectedAvatarIndex = -1;
|
||||
var currentEntity = this.getCurrentAvatarEntity();
|
||||
var existingAvatars = new Int2ObjectOpenHashMap<EntityAvatar>();
|
||||
var prevSelectedAvatarIndex = -1;
|
||||
|
||||
for (EntityAvatar entity : this.getActiveTeam()) {
|
||||
existingAvatars.put(entity.getAvatar().getAvatarId(), entity);
|
||||
@@ -290,9 +316,8 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
|
||||
// Add back entities into team
|
||||
for (int i = 0; i < this.getCurrentTeamInfo().getAvatars().size(); i++) {
|
||||
int avatarId = this.getCurrentTeamInfo().getAvatars().get(i);
|
||||
var avatarId = (int) this.getCurrentTeamInfo().getAvatars().get(i);
|
||||
EntityAvatar entity;
|
||||
|
||||
if (existingAvatars.containsKey(avatarId)) {
|
||||
entity = existingAvatars.get(avatarId);
|
||||
existingAvatars.remove(avatarId);
|
||||
@@ -309,7 +334,7 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
|
||||
// Unload removed entities
|
||||
for (EntityAvatar entity : existingAvatars.values()) {
|
||||
for (var entity : existingAvatars.values()) {
|
||||
this.getPlayer().getScene().removeEntity(entity);
|
||||
entity.getAvatar().save();
|
||||
}
|
||||
@@ -323,18 +348,11 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
this.currentCharacterIndex = prevSelectedAvatarIndex;
|
||||
|
||||
// Update team resonances
|
||||
this.updateTeamResonances();
|
||||
// Update properties.
|
||||
// Notify player.
|
||||
this.updateTeamProperties();
|
||||
|
||||
// Packets
|
||||
this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer()));
|
||||
|
||||
// Skill charges packet - Yes, this is official server behavior as of 2.6.0
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.forEach(Avatar::sendSkillExtraChargeMap);
|
||||
|
||||
// Run callback
|
||||
// Send response packet.
|
||||
if (responsePacket != null) {
|
||||
this.getPlayer().sendPacket(responsePacket);
|
||||
}
|
||||
@@ -402,6 +420,138 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
this.addAvatarsToTeam(teamInfo, newTeam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup avatars for a trial avatar team.
|
||||
*
|
||||
* @param save Should the original team be saved?
|
||||
*/
|
||||
public void setupTrialAvatars(boolean save) {
|
||||
this.setPreviousIndex(this.getCurrentCharacterIndex());
|
||||
|
||||
if (save) {
|
||||
var originalTeam = getCurrentTeamInfo();
|
||||
this.getTrialAvatarTeam().copyFrom(originalTeam);
|
||||
} else this.getActiveTeam().clear();
|
||||
|
||||
this.usingTrialTeam = true;
|
||||
}
|
||||
|
||||
/** Displays the trial avatars. Picks the last avatar in the team. */
|
||||
public void trialAvatarTeamPostUpdate() {
|
||||
this.trialAvatarTeamPostUpdate(this.getActiveTeam().size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the trial avatars.
|
||||
*
|
||||
* @param newCharacterIndex The avatar to equip.
|
||||
*/
|
||||
public void trialAvatarTeamPostUpdate(int newCharacterIndex) {
|
||||
this.setCurrentCharacterIndex(Math.min(newCharacterIndex, this.getActiveTeam().size() - 1));
|
||||
|
||||
this.updateTeamProperties();
|
||||
this.getPlayer().getScene().addEntity(this.getCurrentAvatarEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an avatar to the trial team.
|
||||
*
|
||||
* @param trialAvatar The avatar to add.
|
||||
*/
|
||||
public void addAvatarToTrialTeam(Avatar trialAvatar) {
|
||||
// Remove the existing team's avatars.
|
||||
this.getActiveTeam()
|
||||
.forEach(
|
||||
x ->
|
||||
this.getPlayer()
|
||||
.getScene()
|
||||
.removeEntity(x, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE));
|
||||
// Remove the existing avatar from the teams if it exists.
|
||||
this.getActiveTeam().removeIf(x -> x.getAvatar().getAvatarId() == trialAvatar.getAvatarId());
|
||||
this.getCurrentTeamInfo().getAvatars().removeIf(x -> x == trialAvatar.getAvatarId());
|
||||
// Add the avatar to the teams.
|
||||
this.getActiveTeam().add(new EntityAvatar(this.getPlayer().getScene(), trialAvatar));
|
||||
this.getCurrentTeamInfo().addAvatar(trialAvatar);
|
||||
this.getTrialAvatars().put(trialAvatar.getAvatarId(), trialAvatar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GUID of a trial avatar.
|
||||
*
|
||||
* @param avatarId The avatar ID.
|
||||
* @return The GUID of the avatar.
|
||||
*/
|
||||
public long getTrialAvatarGuid(int avatarId) {
|
||||
return getTrialAvatars().values().stream()
|
||||
.filter(avatar -> avatar.getTrialAvatarId() == avatarId)
|
||||
.map(avatar -> avatar.getGuid())
|
||||
.findFirst()
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
/** Rollback changes from using a trial avatar team. */
|
||||
public void unsetTrialAvatarTeam() {
|
||||
this.trialAvatarTeamPostUpdate(this.getPreviousIndex());
|
||||
this.setPreviousIndex(-1);
|
||||
}
|
||||
|
||||
/** Removes all avatars from the trial avatar team. */
|
||||
public void removeTrialAvatarTeam() {
|
||||
this.removeTrialAvatarTeam(
|
||||
this.getActiveTeam().stream().map(avatar -> avatar.getAvatar().getAvatarId()).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one avatar from the trial avatar team.
|
||||
*
|
||||
* @param avatarId The avatar ID to remove.
|
||||
*/
|
||||
public void removeTrialAvatarTeam(int avatarId) {
|
||||
this.removeTrialAvatarTeam(List.of(avatarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a collection of avatars from the trial avatar team.
|
||||
*
|
||||
* @param avatarIds The avatar IDs to remove.
|
||||
*/
|
||||
public void removeTrialAvatarTeam(List<Integer> avatarIds) {
|
||||
var player = this.getPlayer();
|
||||
|
||||
// Disable the trial team.
|
||||
this.usingTrialTeam = false;
|
||||
this.trialAvatarTeam = new TeamInfo();
|
||||
|
||||
// Remove the avatars from the team.
|
||||
avatarIds.forEach(
|
||||
avatarId -> {
|
||||
this.getActiveTeam()
|
||||
.forEach(
|
||||
x ->
|
||||
player
|
||||
.getScene()
|
||||
.removeEntity(x, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE));
|
||||
this.getActiveTeam().removeIf(x -> x.getAvatar().getTrialAvatarId() == avatarId);
|
||||
this.getTrialAvatars().values().removeIf(x -> x.getTrialAvatarId() == avatarId);
|
||||
});
|
||||
|
||||
// Re-add the avatars to the team.
|
||||
var index = 0;
|
||||
for (var avatar : this.getCurrentTeamInfo().getAvatars()) {
|
||||
if (this.getActiveTeam().stream()
|
||||
.map(entity -> entity.getAvatar().getAvatarId())
|
||||
.toList()
|
||||
.contains(avatar)) return;
|
||||
|
||||
this.getActiveTeam()
|
||||
.add(
|
||||
index++,
|
||||
new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatar)));
|
||||
}
|
||||
|
||||
this.unsetTrialAvatarTeam();
|
||||
}
|
||||
|
||||
public void setupTemporaryTeam(List<List<Long>> guidList) {
|
||||
this.temporaryTeam =
|
||||
guidList.stream()
|
||||
@@ -725,4 +875,195 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
player.sendPacket(new PacketAvatarTeamAllDataNotify(player));
|
||||
player.sendPacket(new PacketDelBackupAvatarTeamRsp(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies abilities for the currently selected team. These abilities are sourced from the scene.
|
||||
*
|
||||
* @param scene The scene with the abilities to apply.
|
||||
*/
|
||||
public void applyAbilities(Scene scene) {
|
||||
try {
|
||||
var levelEntityConfig = scene.getSceneData().getLevelEntityConfig();
|
||||
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
|
||||
if (config == null) return;
|
||||
|
||||
var avatars = this.getPlayer().getAvatars();
|
||||
var avatarIds = scene.getSceneData().getSpecifiedAvatarList();
|
||||
var specifiedAvatarList = this.getActiveTeam();
|
||||
|
||||
if (avatarIds != null && avatarIds.size() > 0) {
|
||||
// certain scene could limit specific avatars' entry
|
||||
specifiedAvatarList.clear();
|
||||
for (int id : avatarIds) {
|
||||
var avatar = avatars.getAvatarById(id);
|
||||
if (avatar == null) continue;
|
||||
|
||||
specifiedAvatarList.add(new EntityAvatar(scene, avatar));
|
||||
}
|
||||
}
|
||||
|
||||
for (var entityAvatar : specifiedAvatarList) {
|
||||
var avatarData = entityAvatar.getAvatar().getAvatarData();
|
||||
if (avatarData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
avatarData.buildEmbryo(); // Create avatar abilities.
|
||||
if (config.getAvatarAbilities() == null) {
|
||||
continue; // continue and not break because has to rebuild ability for the next avatar if
|
||||
// any
|
||||
}
|
||||
|
||||
for (ConfigAbilityData abilities : config.getAvatarAbilities()) {
|
||||
avatarData.getAbilities().add(Utils.abilityHash(abilities.getAbilityName()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger()
|
||||
.error(
|
||||
"Error applying level entity config for scene {}", scene.getSceneData().getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> getTrialAvatarParam(int trialAvatarId) {
|
||||
if (GameData.getTrialAvatarCustomData()
|
||||
.isEmpty()) { // use default data if custom data not available
|
||||
if (GameData.getTrialAvatarDataMap().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
return GameData.getTrialAvatarDataMap().get(trialAvatarId).getTrialAvatarParamList();
|
||||
}
|
||||
// use custom data
|
||||
if (GameData.getTrialAvatarCustomData().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
val trialCustomParams =
|
||||
GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
|
||||
return trialCustomParams.isEmpty()
|
||||
? List.of()
|
||||
: Stream.of(trialCustomParams.get(0).split(";")).map(Integer::parseInt).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trial avatar to the player's team.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
* @param questMainId The quest ID associated with the quest.
|
||||
* @param reason The reason for granting the avatar.
|
||||
* @return True if the avatar was added, false otherwise.
|
||||
*/
|
||||
public boolean addTrialAvatar(int avatarId, int questMainId, GrantReason reason) {
|
||||
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(avatarId);
|
||||
if (trialAvatarBasicParam.isEmpty()) return false;
|
||||
|
||||
var avatar = new Avatar(trialAvatarBasicParam.get(0));
|
||||
if (avatar.getAvatarData() == null || !this.getPlayer().hasSentLoginPackets()) return false;
|
||||
|
||||
avatar.setOwner(this.getPlayer());
|
||||
// Add trial weapons and relics.
|
||||
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), avatarId, reason, questMainId);
|
||||
avatar.equipTrialItems();
|
||||
// Re-calculate stats
|
||||
avatar.recalcStats();
|
||||
|
||||
// Packet, mimic official server behaviour, add to player's bag but not saving to database.
|
||||
this.getPlayer().sendPacket(new PacketAvatarAddNotify(avatar, false));
|
||||
// Add to avatar to the temporary trial team.
|
||||
this.addAvatarToTrialTeam(avatar);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trial avatar to the player's team.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
* @param questMainId The quest ID associated with the quest.
|
||||
*/
|
||||
public void addTrialAvatar(int avatarId, int questMainId) {
|
||||
this.addTrialAvatars(List.of(avatarId), questMainId, true);
|
||||
|
||||
// Packet, mimic official server behaviour, necessary to stop player from modifying team.
|
||||
this.getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds) {
|
||||
this.addTrialAvatars(avatarIds, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
* @param save Whether to retain the currently equipped avatars.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds, boolean save) {
|
||||
this.addTrialAvatars(avatarIds, 0, save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
* @param questId The ID of the quest this trial team is associated with.
|
||||
* @param save Whether to retain the currently equipped avatars.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds, int questId, boolean save) {
|
||||
this.setupTrialAvatars(save); // Perform initial setup.
|
||||
|
||||
// Add the avatars to the team.
|
||||
avatarIds.forEach(
|
||||
avatarId -> {
|
||||
var result =
|
||||
this.addTrialAvatar(
|
||||
avatarId,
|
||||
questId,
|
||||
questId == 0
|
||||
? GrantReason.GRANT_REASON_BY_QUEST
|
||||
: GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY);
|
||||
|
||||
if (!result) throw new RuntimeException("Unable to add trial avatar to team.");
|
||||
});
|
||||
|
||||
// Update the team.
|
||||
this.trialAvatarTeamPostUpdate(questId == 0 ? getActiveTeam().size() - 1 : 0);
|
||||
}
|
||||
|
||||
/** Removes all trial avatars from the player's team. */
|
||||
public void removeTrialAvatar() {
|
||||
this.removeTrialAvatar(
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.map(Avatar::getAvatarId)
|
||||
.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a trial avatar from the player's team. Additionally, unlocks the ability to change the
|
||||
* team configuration.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
*/
|
||||
public void removeTrialAvatar(int avatarId) {
|
||||
this.removeTrialAvatar(List.of(avatarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a collection of trial avatars from the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
*/
|
||||
public void removeTrialAvatar(List<Integer> avatarIds) {
|
||||
if (!this.isUsingTrialTeam()) throw new RuntimeException("Player is not using a trial team.");
|
||||
|
||||
this.getPlayer()
|
||||
.sendPacket(
|
||||
new PacketAvatarDelNotify(avatarIds.stream().map(this::getTrialAvatarGuid).toList()));
|
||||
this.removeTrialAvatarTeam(avatarIds);
|
||||
|
||||
// Update the team.
|
||||
if (avatarIds.size() == 1) this.getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,17 @@ import lombok.Getter;
|
||||
@AllArgsConstructor
|
||||
public enum ActivityType {
|
||||
NONE(0),
|
||||
NEW_ACTIVITY_TRIAL_AVATAR(4),
|
||||
NEW_ACTIVITY_PERSONAL_LIINE(8),
|
||||
NEW_ACTIVITY_SALESMAN_MP(1205),
|
||||
NEW_ACTIVITY_SUMMER_TIME(1600),
|
||||
NEW_ACTIVITY_GENERAL_BANNER(2100),
|
||||
NEW_ACTIVITY_MUSIC_GAME(2202),
|
||||
;
|
||||
NEW_ACTIVITY_PHOTO(2603),
|
||||
NEW_ACTIVITY_FUNGUS_FIGHTER(3201),
|
||||
NEW_ACTIVITY_EFFIGY_CHALLENGE_V2(3203);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<ActivityType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, ActivityType> stringMap = new HashMap<>();
|
||||
|
||||
@@ -27,8 +35,6 @@ public enum ActivityType {
|
||||
});
|
||||
}
|
||||
|
||||
private final int value;
|
||||
|
||||
public static ActivityType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, NONE);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.game.props.ItemUseAction;
|
||||
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
|
||||
@@ -1,43 +1,44 @@
|
||||
package emu.grasscutter.game.props;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public enum RefreshType {
|
||||
REFRESH_NONE (0),
|
||||
REFRESH_INTERVAL (1),
|
||||
REFRESH_DAILY (2),
|
||||
REFRESH_WEEKlY (3),
|
||||
REFRESH_DAYBEGIN_INTERVAL (4);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<RefreshType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, RefreshType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values()).forEach(e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private RefreshType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static RefreshType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, REFRESH_NONE);
|
||||
}
|
||||
|
||||
public static RefreshType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, REFRESH_NONE);
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.props;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum RefreshType {
|
||||
REFRESH_NONE(0),
|
||||
REFRESH_INTERVAL(1),
|
||||
REFRESH_DAILY(2),
|
||||
REFRESH_WEEKlY(3),
|
||||
REFRESH_DAYBEGIN_INTERVAL(4);
|
||||
|
||||
private final int value;
|
||||
private static final Int2ObjectMap<RefreshType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, RefreshType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values())
|
||||
.forEach(
|
||||
e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private RefreshType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static RefreshType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, REFRESH_NONE);
|
||||
}
|
||||
|
||||
public static RefreshType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, REFRESH_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,443 +1,490 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.MainQuestData;
|
||||
import emu.grasscutter.data.binout.MainQuestData.SubQuestData;
|
||||
import emu.grasscutter.data.binout.MainQuestData.TalkData;
|
||||
import emu.grasscutter.data.binout.ScriptSceneData;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.RewardData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.LogicType;
|
||||
import emu.grasscutter.game.quest.enums.ParentQuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
|
||||
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import java.util.*;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptException;
|
||||
import lombok.Getter;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
@Entity(value = "quests", useDiscriminator = false)
|
||||
public class GameMainQuest {
|
||||
@Getter List<QuestGroupSuite> questGroupSuites;
|
||||
@Getter int[] suggestTrackMainQuestList;
|
||||
@Id private ObjectId id;
|
||||
@Indexed @Getter private int ownerUid;
|
||||
@Transient @Getter private Player owner;
|
||||
@Transient @Getter private QuestManager questManager;
|
||||
@Getter private Map<Integer, GameQuest> childQuests;
|
||||
@Getter private int parentQuestId;
|
||||
@Getter private int[] questVars;
|
||||
// QuestUpdateQuestVarReq is sent in two stages...
|
||||
@Getter private List<Integer> questVarsUpdate;
|
||||
@Getter private ParentQuestState state;
|
||||
@Getter private boolean isFinished;
|
||||
@Getter private Map<Integer, TalkData> talks;
|
||||
// key is subId
|
||||
private Map<Integer, Position> rewindPositions;
|
||||
private Map<Integer, Position> rewindRotations;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameMainQuest() {}
|
||||
|
||||
public GameMainQuest(Player player, int parentQuestId) {
|
||||
this.owner = player;
|
||||
this.ownerUid = player.getUid();
|
||||
this.questManager = player.getQuestManager();
|
||||
this.parentQuestId = parentQuestId;
|
||||
this.childQuests = new HashMap<>();
|
||||
this.talks = new HashMap<>();
|
||||
// official server always has a list of 5 questVars, with default value 0
|
||||
this.questVars = new int[] {0, 0, 0, 0, 0};
|
||||
this.state = ParentQuestState.PARENT_QUEST_STATE_NONE;
|
||||
this.questGroupSuites = new ArrayList<>();
|
||||
this.rewindPositions = new HashMap<>();
|
||||
this.rewindRotations = new HashMap<>();
|
||||
addAllChildQuests();
|
||||
addRewindPoints();
|
||||
}
|
||||
|
||||
private void addAllChildQuests() {
|
||||
List<Integer> subQuestIds =
|
||||
Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests())
|
||||
.map(SubQuestData::getSubId)
|
||||
.toList();
|
||||
for (Integer subQuestId : subQuestIds) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(subQuestId);
|
||||
this.childQuests.put(subQuestId, new GameQuest(this, questConfig));
|
||||
}
|
||||
}
|
||||
|
||||
public void setOwner(Player player) {
|
||||
if (player.getUid() != this.getOwnerUid()) return;
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
public int getQuestVar(int i) {
|
||||
return questVars[i];
|
||||
}
|
||||
|
||||
public void setQuestVar(int i, int value) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] = value;
|
||||
Grasscutter.getLogger()
|
||||
.debug("questVar {} value changed from {} to {}", i, previousValue, value);
|
||||
}
|
||||
|
||||
public void incQuestVar(int i, int inc) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] += inc;
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"questVar {} value incremented from {} to {}", i, previousValue, previousValue + inc);
|
||||
}
|
||||
|
||||
public void decQuestVar(int i, int dec) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] -= dec;
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"questVar {} value decremented from {} to {}", i, previousValue, previousValue - dec);
|
||||
}
|
||||
|
||||
public GameQuest getChildQuestById(int id) {
|
||||
return this.getChildQuests().get(id);
|
||||
}
|
||||
|
||||
public GameQuest getChildQuestByOrder(int order) {
|
||||
return this.getChildQuests().values().stream()
|
||||
.filter(p -> p.getQuestData().getOrder() == order)
|
||||
.toList()
|
||||
.get(0);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.isFinished = true;
|
||||
this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED;
|
||||
|
||||
this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this));
|
||||
this.getOwner().getSession().send(new PacketCodexDataUpdateNotify(this));
|
||||
|
||||
this.save();
|
||||
|
||||
// Add rewards
|
||||
MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(this.getParentQuestId());
|
||||
if (mainQuestData != null && mainQuestData.getRewardIdList() != null) {
|
||||
for (int rewardId : mainQuestData.getRewardIdList()) {
|
||||
RewardData rewardData = GameData.getRewardDataMap().get(rewardId);
|
||||
|
||||
if (rewardData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
getOwner()
|
||||
.getInventory()
|
||||
.addItemParamDatas(rewardData.getRewardItemList(), ActionReason.QuestReward);
|
||||
}
|
||||
}
|
||||
|
||||
// handoff main quest
|
||||
if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
.forEach(getQuestManager()::startMainQuest);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void fail() {}
|
||||
|
||||
public void cancel() {}
|
||||
|
||||
// Rewinds to the last finished/unfinished rewind quest, and returns the avatar rewind position
|
||||
// (if it exists)
|
||||
public List<Position> rewind() {
|
||||
if (this.questManager == null) {
|
||||
this.questManager = getOwner().getQuestManager();
|
||||
}
|
||||
List<GameQuest> sortedByOrder =
|
||||
new ArrayList<>(
|
||||
getChildQuests().values().stream().filter(q -> q.getQuestData().isRewind()).toList());
|
||||
sortedByOrder.sort(
|
||||
(a, b) -> {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
}
|
||||
return a.getQuestData().getOrder() > b.getQuestData().getOrder() ? 1 : -1;
|
||||
});
|
||||
boolean didRewind = false;
|
||||
for (GameQuest quest : sortedByOrder) {
|
||||
int i = sortedByOrder.indexOf(quest);
|
||||
if ((i + 1) >= sortedByOrder.size()) {
|
||||
didRewind = quest.rewind(null);
|
||||
} else {
|
||||
didRewind = quest.rewind(sortedByOrder.get(i + 1));
|
||||
}
|
||||
if (didRewind) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<GameQuest> rewindQuests =
|
||||
getChildQuests().values().stream()
|
||||
.filter(
|
||||
p ->
|
||||
(p.getState() == QuestState.QUEST_STATE_UNFINISHED
|
||||
|| p.getState() == QuestState.QUEST_STATE_FINISHED)
|
||||
&& p.getQuestData().isRewind())
|
||||
.toList();
|
||||
for (GameQuest quest : rewindQuests) {
|
||||
if (rewindPositions.containsKey(quest.getSubQuestId())) {
|
||||
List<Position> posAndRot = new ArrayList<>();
|
||||
posAndRot.add(0, rewindPositions.get(quest.getSubQuestId()));
|
||||
posAndRot.add(1, rewindRotations.get(quest.getSubQuestId()));
|
||||
return posAndRot;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addRewindPoints() {
|
||||
Bindings bindings = ScriptLoader.getEngine().createBindings();
|
||||
String script = "Quest/Share/Q" + getParentQuestId() + "ShareConfig.lua";
|
||||
CompiledScript cs = ScriptLoader.getScript(script);
|
||||
|
||||
// mainQuest 303 doesn't have a ShareConfig
|
||||
if (cs == null) {
|
||||
Grasscutter.getLogger().debug("Couldn't find " + script);
|
||||
return;
|
||||
}
|
||||
|
||||
// Eval script
|
||||
try {
|
||||
cs.eval(bindings);
|
||||
|
||||
var rewindDataMap =
|
||||
ScriptLoader.getSerializer().toMap(RewindData.class, bindings.get("rewind_data"));
|
||||
for (String subId : rewindDataMap.keySet()) {
|
||||
RewindData questRewind = rewindDataMap.get(subId);
|
||||
if (questRewind != null) {
|
||||
RewindData.AvatarData avatarData = questRewind.getAvatar();
|
||||
if (avatarData != null) {
|
||||
String avatarPos = avatarData.getPos();
|
||||
QuestData.Guide guide =
|
||||
GameData.getQuestDataMap().get(Integer.valueOf(subId)).getGuide();
|
||||
if (guide != null) {
|
||||
int sceneId = guide.getGuideScene();
|
||||
ScriptSceneData fullGlobals =
|
||||
GameData.getScriptSceneDataMap().get("flat.luas.scenes.full_globals.lua.json");
|
||||
if (fullGlobals != null) {
|
||||
ScriptSceneData.ScriptObject dummyPointScript =
|
||||
fullGlobals
|
||||
.getScriptObjectList()
|
||||
.get(sceneId + "/scene" + sceneId + "_dummy_points.lua");
|
||||
if (dummyPointScript != null) {
|
||||
Map<String, List<Float>> dummyPointMap = dummyPointScript.getDummyPoints();
|
||||
if (dummyPointMap != null) {
|
||||
List<Float> avatarPosPos = dummyPointMap.get(avatarPos + ".pos");
|
||||
if (avatarPosPos != null) {
|
||||
Position pos =
|
||||
new Position(
|
||||
avatarPosPos.get(0), avatarPosPos.get(1), avatarPosPos.get(2));
|
||||
List<Float> avatarPosRot = dummyPointMap.get(avatarPos + ".rot");
|
||||
Position rot =
|
||||
new Position(
|
||||
avatarPosRot.get(0), avatarPosRot.get(1), avatarPosRot.get(2));
|
||||
rewindPositions.put(Integer.valueOf(subId), pos);
|
||||
rewindRotations.put(Integer.valueOf(subId), rot);
|
||||
Grasscutter.getLogger()
|
||||
.debug("Succesfully loaded rewind position for subQuest {}", subId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ScriptException e) {
|
||||
Grasscutter.getLogger().error("An error occurred while loading rewind positions");
|
||||
}
|
||||
}
|
||||
|
||||
public void tryAcceptSubQuests(QuestTrigger condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond =
|
||||
getChildQuests().values().stream()
|
||||
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNSTARTED)
|
||||
.filter(
|
||||
p ->
|
||||
p.getQuestData().getAcceptCond().stream()
|
||||
.anyMatch(q -> q.getType() == condType))
|
||||
.toList();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
List<QuestData.QuestCondition> acceptCond = subQuestWithCond.getQuestData().getAcceptCond();
|
||||
int[] accept = new int[acceptCond.size()];
|
||||
|
||||
for (int i = 0; i < subQuestWithCond.getQuestData().getAcceptCond().size(); i++) {
|
||||
QuestData.QuestCondition condition = acceptCond.get(i);
|
||||
boolean result =
|
||||
this.getOwner()
|
||||
.getServer()
|
||||
.getQuestSystem()
|
||||
.triggerCondition(subQuestWithCond, condition, paramStr, params);
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
|
||||
boolean shouldAccept =
|
||||
LogicType.calculate(subQuestWithCond.getQuestData().getAcceptCondComb(), accept);
|
||||
|
||||
if (shouldAccept) {
|
||||
subQuestWithCond.start();
|
||||
getQuestManager().getAddToQuestListUpdateNotify().add(subQuestWithCond);
|
||||
}
|
||||
}
|
||||
this.save();
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void tryFailSubQuests(QuestTrigger condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond =
|
||||
getChildQuests().values().stream()
|
||||
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNFINISHED)
|
||||
.filter(
|
||||
p ->
|
||||
p.getQuestData().getFailCond().stream()
|
||||
.anyMatch(q -> q.getType() == condType))
|
||||
.toList();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
List<QuestData.QuestCondition> failCond = subQuestWithCond.getQuestData().getFailCond();
|
||||
|
||||
for (int i = 0; i < subQuestWithCond.getQuestData().getFailCond().size(); i++) {
|
||||
QuestData.QuestCondition condition = failCond.get(i);
|
||||
if (condition.getType() == condType) {
|
||||
boolean result =
|
||||
this.getOwner()
|
||||
.getServer()
|
||||
.getQuestSystem()
|
||||
.triggerContent(subQuestWithCond, condition, paramStr, params);
|
||||
subQuestWithCond.getFailProgressList()[i] = result ? 1 : 0;
|
||||
if (result) {
|
||||
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldFail =
|
||||
LogicType.calculate(
|
||||
subQuestWithCond.getQuestData().getFailCondComb(),
|
||||
subQuestWithCond.getFailProgressList());
|
||||
|
||||
if (shouldFail) {
|
||||
subQuestWithCond.fail();
|
||||
getQuestManager().getAddToQuestListUpdateNotify().add(subQuestWithCond);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to fail quest.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void tryFinishSubQuests(QuestTrigger condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond =
|
||||
getChildQuests().values().stream()
|
||||
// There are subQuests with no acceptCond, but can be finished (example: 35104)
|
||||
.filter(
|
||||
p ->
|
||||
p.getState() == QuestState.QUEST_STATE_UNFINISHED
|
||||
&& p.getQuestData().getAcceptCond() != null)
|
||||
.filter(
|
||||
p ->
|
||||
p.getQuestData().getFinishCond().stream()
|
||||
.anyMatch(q -> q.getType() == condType))
|
||||
.toList();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
List<QuestData.QuestCondition> finishCond = subQuestWithCond.getQuestData().getFinishCond();
|
||||
|
||||
for (int i = 0; i < finishCond.size(); i++) {
|
||||
QuestData.QuestCondition condition = finishCond.get(i);
|
||||
if (condition.getType() == condType) {
|
||||
boolean result =
|
||||
this.getOwner()
|
||||
.getServer()
|
||||
.getQuestSystem()
|
||||
.triggerContent(subQuestWithCond, condition, paramStr, params);
|
||||
subQuestWithCond.getFinishProgressList()[i] = result ? 1 : 0;
|
||||
if (result) {
|
||||
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldFinish =
|
||||
LogicType.calculate(
|
||||
subQuestWithCond.getQuestData().getFinishCondComb(),
|
||||
subQuestWithCond.getFinishProgressList());
|
||||
|
||||
if (shouldFinish) {
|
||||
subQuestWithCond.finish();
|
||||
getQuestManager().getAddToQuestListUpdateNotify().add(subQuestWithCond);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().debug("An error occurred while trying to finish quest.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveQuest(this);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
DatabaseHelper.deleteQuest(this);
|
||||
}
|
||||
|
||||
public ParentQuest toProto() {
|
||||
ParentQuest.Builder proto =
|
||||
ParentQuest.newBuilder().setParentQuestId(getParentQuestId()).setIsFinished(isFinished());
|
||||
|
||||
proto
|
||||
.setParentQuestState(getState().getValue())
|
||||
.setVideoKey(QuestManager.getQuestKey(parentQuestId));
|
||||
for (GameQuest quest : this.getChildQuests().values()) {
|
||||
if (quest.getState() != QuestState.QUEST_STATE_UNSTARTED) {
|
||||
ChildQuest childQuest =
|
||||
ChildQuest.newBuilder()
|
||||
.setQuestId(quest.getSubQuestId())
|
||||
.setState(quest.getState().getValue())
|
||||
.build();
|
||||
|
||||
proto.addChildQuestList(childQuest);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : getQuestVars()) {
|
||||
proto.addQuestVar(i);
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import dev.morphia.annotations.Indexed;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.MainQuestData;
|
||||
import emu.grasscutter.data.binout.MainQuestData.SubQuestData;
|
||||
import emu.grasscutter.data.binout.MainQuestData.TalkData;
|
||||
import emu.grasscutter.data.binout.ScriptSceneData;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.RewardData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.*;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
|
||||
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
|
||||
import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
|
||||
import emu.grasscutter.utils.ConversionUtils;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "quests", useDiscriminator = false)
|
||||
public class GameMainQuest {
|
||||
@Id private ObjectId id;
|
||||
@Indexed @Getter private int ownerUid;
|
||||
@Transient @Getter private Player owner;
|
||||
@Transient @Getter private QuestManager questManager;
|
||||
@Getter private Map<Integer, GameQuest> childQuests;
|
||||
@Getter private int parentQuestId;
|
||||
@Getter private int[] questVars;
|
||||
@Getter private long[] timeVar;
|
||||
//QuestUpdateQuestVarReq is sent in two stages...
|
||||
@Getter private List<Integer> questVarsUpdate;
|
||||
@Getter private ParentQuestState state;
|
||||
@Getter private boolean isFinished;
|
||||
@Getter List<QuestGroupSuite> questGroupSuites;
|
||||
|
||||
@Getter int[] suggestTrackMainQuestList;
|
||||
@Getter private Map<Integer,TalkData> talks;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameMainQuest() {}
|
||||
|
||||
public GameMainQuest(Player player, int parentQuestId) {
|
||||
this.owner = player;
|
||||
this.ownerUid = player.getUid();
|
||||
this.questManager = player.getQuestManager();
|
||||
this.parentQuestId = parentQuestId;
|
||||
this.childQuests = new HashMap<>();
|
||||
this.talks = new HashMap<>();
|
||||
//official server always has a list of 5 questVars, with default value 0
|
||||
this.questVars = new int[] {0,0,0,0,0};
|
||||
this.timeVar = new long[] {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; // theoretically max is 10 here
|
||||
this.state = ParentQuestState.PARENT_QUEST_STATE_NONE;
|
||||
this.questGroupSuites = new ArrayList<>();
|
||||
addAllChildQuests();
|
||||
}
|
||||
|
||||
private void addAllChildQuests() {
|
||||
List<Integer> subQuestIds = Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests()).map(SubQuestData::getSubId).toList();
|
||||
for (Integer subQuestId : subQuestIds) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(subQuestId);
|
||||
this.childQuests.put(subQuestId, new GameQuest(this, questConfig));
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<GameQuest> getActiveQuests(){
|
||||
return childQuests.values().stream()
|
||||
.filter(q->q.getState().getValue() == QuestState.QUEST_STATE_UNFINISHED.getValue())
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void setOwner(Player player) {
|
||||
if (player.getUid() != this.getOwnerUid()) return;
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
public int getQuestVar(int i) {
|
||||
return questVars[i];
|
||||
}
|
||||
public void setQuestVar(int i, int value) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] = value;
|
||||
Grasscutter.getLogger().debug("questVar {} value changed from {} to {}", i, previousValue, value);
|
||||
}
|
||||
|
||||
public void incQuestVar(int i, int inc) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] += inc;
|
||||
Grasscutter.getLogger().debug("questVar {} value incremented from {} to {}", i, previousValue, previousValue + inc);
|
||||
}
|
||||
|
||||
public void decQuestVar(int i, int dec) {
|
||||
int previousValue = this.questVars[i];
|
||||
this.questVars[i] -= dec;
|
||||
Grasscutter.getLogger().debug("questVar {} value decremented from {} to {}", i, previousValue, previousValue - dec);
|
||||
}
|
||||
|
||||
|
||||
public GameQuest getChildQuestById(int id) {
|
||||
return this.getChildQuests().get(id);
|
||||
}
|
||||
public GameQuest getChildQuestByOrder(int order) {
|
||||
return this.getChildQuests().values().stream().filter(p -> p.getQuestData().getOrder() == order).toList().get(0);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
// Avoid recursion from child finish() in GameQuest
|
||||
// when auto finishing all child quests with QUEST_STATE_UNFINISHED (below)
|
||||
if (this.isFinished) {
|
||||
Grasscutter.getLogger().debug("Skip main quest finishing because it's already finished");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isFinished = true;
|
||||
this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED;
|
||||
|
||||
/*
|
||||
We also need to check for unfinished childQuests in this MainQuest
|
||||
force them to complete and send a packet about this to the user,
|
||||
because at some points there are special "invisible" child quests that control
|
||||
some situations.
|
||||
|
||||
For example, subQuest 35312 is responsible for the event of leaving the territory
|
||||
of the island with a statue and automatically returns the character back,
|
||||
quest 35311 completes the main quest line 353 and starts 35501 from
|
||||
new MainQuest 355 but if 35312 is not completed after the completion
|
||||
of the main quest 353 - the character will not be able to leave place
|
||||
(return again and again)
|
||||
*/
|
||||
this
|
||||
.getChildQuests()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(p -> p.state != QuestState.QUEST_STATE_FINISHED)
|
||||
.forEach(GameQuest::finish);
|
||||
|
||||
this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this));
|
||||
this.getOwner().getSession().send(new PacketCodexDataUpdateNotify(this));
|
||||
|
||||
this.save();
|
||||
|
||||
// Add rewards
|
||||
MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(this.getParentQuestId());
|
||||
if(mainQuestData.getRewardIdList()!=null) {
|
||||
for (int rewardId : mainQuestData.getRewardIdList()) {
|
||||
RewardData rewardData = GameData.getRewardDataMap().get(rewardId);
|
||||
|
||||
if (rewardData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
getOwner().getInventory().addItemParamDatas(rewardData.getRewardItemList(), ActionReason.QuestReward);
|
||||
}
|
||||
}
|
||||
|
||||
// handoff main quest
|
||||
if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
.forEach(getQuestManager()::startMainQuest);
|
||||
}
|
||||
}
|
||||
//TODO
|
||||
public void fail() {}
|
||||
public void cancel() {}
|
||||
|
||||
|
||||
public List<Position> rewindTo(GameQuest targetQuest, boolean notifyDelete){
|
||||
if(targetQuest == null || !targetQuest.rewind(notifyDelete)){
|
||||
return null;
|
||||
}
|
||||
|
||||
/*if(rewindPositions.isEmpty()){
|
||||
addRewindPoints();
|
||||
}*/
|
||||
|
||||
List<Position> posAndRot = new ArrayList<>();
|
||||
if(hasRewindPosition(targetQuest.getSubQuestId(), posAndRot)){
|
||||
return posAndRot;
|
||||
}
|
||||
|
||||
List<GameQuest> rewindQuests = getChildQuests().values().stream()
|
||||
.filter(p -> (p.getState() == QuestState.QUEST_STATE_UNFINISHED || p.getState() == QuestState.QUEST_STATE_FINISHED) &&
|
||||
p.getQuestData() != null && p.getQuestData().isRewind()).toList();
|
||||
|
||||
for (GameQuest quest : rewindQuests) {
|
||||
if (hasRewindPosition(quest.getSubQuestId(), posAndRot)) {
|
||||
return posAndRot;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Rewinds to the last finished/unfinished rewind quest, and returns the avatar rewind position (if it exists)
|
||||
public List<Position> rewind() {
|
||||
if (this.questManager == null) {
|
||||
this.questManager = getOwner().getQuestManager();
|
||||
}
|
||||
var activeQuests = getActiveQuests();
|
||||
var highestActiveQuest = activeQuests.stream()
|
||||
.filter(q -> q.getQuestData() != null)
|
||||
.max(Comparator.comparing(q -> q.getQuestData().getOrder()))
|
||||
.orElse(null);
|
||||
|
||||
if (highestActiveQuest == null) {
|
||||
var firstUnstarted = getChildQuests().values().stream()
|
||||
.filter(q -> q.getQuestData() != null && q.getState().getValue() != QuestState.FINISHED.getValue())
|
||||
.min(Comparator.comparingInt(a -> a.getQuestData().getOrder()));
|
||||
if(firstUnstarted.isEmpty()){
|
||||
// all quests are probably finished, do don't rewind and maybe also set the mainquest to finished?
|
||||
return null;
|
||||
}
|
||||
highestActiveQuest = firstUnstarted.get();
|
||||
//todo maybe try to accept quests if there is no active quest and no rewind target?
|
||||
//tryAcceptSubQuests(QuestTrigger.QUEST_COND_NONE, "", 0);
|
||||
}
|
||||
|
||||
var highestOrder = highestActiveQuest.getQuestData().getOrder();
|
||||
var rewindTarget = getChildQuests().values().stream()
|
||||
.filter(q -> q.getQuestData() != null)
|
||||
.filter(q -> q.getQuestData().isRewind() && q.getQuestData().getOrder() <= highestOrder)
|
||||
.max(Comparator.comparingInt(a -> a.getQuestData().getOrder()))
|
||||
.orElse(null);
|
||||
|
||||
return rewindTo(rewindTarget!=null? rewindTarget : highestActiveQuest, false);
|
||||
}
|
||||
|
||||
public boolean hasRewindPosition(int subId, List<Position> posAndRot){
|
||||
RewindData questRewind = GameData.getRewindDataMap().get(subId);
|
||||
if (questRewind == null) return false;
|
||||
|
||||
RewindData.AvatarData avatarData = questRewind.getAvatar();
|
||||
if (avatarData == null) return false;
|
||||
|
||||
String avatarPos = avatarData.getPos();
|
||||
QuestData.Guide guide = GameData.getQuestDataMap().get(subId).getGuide();
|
||||
if (guide == null) return false;
|
||||
|
||||
int sceneId = guide.getGuideScene();
|
||||
ScriptSceneData fullGlobals = GameData.getScriptSceneDataMap().get("flat.luas.scenes.full_globals.lua.json");
|
||||
if (fullGlobals == null) return false;
|
||||
|
||||
ScriptSceneData.ScriptObject dummyPointScript = fullGlobals.getScriptObjectList().get(sceneId + "/scene" + sceneId + "_dummy_points.lua");
|
||||
if (dummyPointScript == null) return false;
|
||||
|
||||
Map<String, List<Float>> dummyPointMap = dummyPointScript.getDummyPoints();
|
||||
if (dummyPointMap == null) return false;
|
||||
|
||||
List<Float> avatarPosPos = dummyPointMap.get(avatarPos + ".pos");
|
||||
List<Float> avatarPosRot = dummyPointMap.get(avatarPos + ".rot");
|
||||
if (avatarPosPos == null) return false;
|
||||
|
||||
posAndRot.add(0, new Position(avatarPosPos.get(0),avatarPosPos.get(1),avatarPosPos.get(2))); // position
|
||||
posAndRot.add(1, new Position(avatarPosRot.get(0),avatarPosRot.get(1),avatarPosRot.get(2))); //rotation
|
||||
Grasscutter.getLogger().info("Succesfully loaded rewind data for subQuest {}", subId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasTeleportPostion(int subId, List<Position> posAndRot){
|
||||
TeleportData questTransmit = GameData.getTeleportDataMap().get(subId);
|
||||
if (questTransmit == null) return false;
|
||||
|
||||
TeleportData.TransmitPoint transmitPoint = questTransmit.getTransmit_points().size() > 0 ? questTransmit.getTransmit_points().get(0) : null;
|
||||
if (transmitPoint == null) return false;
|
||||
|
||||
String transmitPos = transmitPoint.getPos();
|
||||
int sceneId = transmitPoint.getScene_id();
|
||||
ScriptSceneData fullGlobals = GameData.getScriptSceneDataMap().get("flat.luas.scenes.full_globals.lua.json");
|
||||
if (fullGlobals == null) return false;
|
||||
|
||||
ScriptSceneData.ScriptObject dummyPointScript = fullGlobals.getScriptObjectList().get(sceneId + "/scene" + sceneId + "_dummy_points.lua");
|
||||
if (dummyPointScript == null) return false;
|
||||
|
||||
Map<String, List<Float>> dummyPointMap = dummyPointScript.getDummyPoints();
|
||||
if (dummyPointMap == null) return false;
|
||||
|
||||
List<Float> transmitPosPos = dummyPointMap.get(transmitPos + ".pos");
|
||||
List<Float> transmitPosRot = dummyPointMap.get(transmitPos + ".rot");
|
||||
if (transmitPosPos == null) return false;
|
||||
|
||||
posAndRot.add(0, new Position(transmitPosPos.get(0), transmitPosPos.get(1), transmitPosPos.get(2))); // position
|
||||
posAndRot.add(1, new Position(transmitPosRot.get(0), transmitPosRot.get(1), transmitPosRot.get(2))); // rotation
|
||||
Grasscutter.getLogger().info("Succesfully loaded teleport data for subQuest {}", subId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void checkProgress(){
|
||||
for (var quest : getChildQuests().values()){
|
||||
if(quest.getState() == QuestState.QUEST_STATE_UNFINISHED) {
|
||||
questManager.checkQuestAlreadyFullfilled(quest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void tryAcceptSubQuests(QuestCond condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond = getChildQuests().values().stream()
|
||||
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNSTARTED || p.getState() == QuestState.UNFINISHED)
|
||||
.filter(p -> p.getQuestData().getAcceptCond().stream().anyMatch(q -> condType == QuestCond.QUEST_COND_NONE || q.getType() == condType))
|
||||
.toList();
|
||||
var questSystem = owner.getServer().getQuestSystem();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
var acceptCond = subQuestWithCond.getQuestData().getAcceptCond();
|
||||
int[] accept = new int[acceptCond.size()];
|
||||
|
||||
for (int i = 0; i < subQuestWithCond.getQuestData().getAcceptCond().size(); i++) {
|
||||
var condition = acceptCond.get(i);
|
||||
boolean result = questSystem.triggerCondition(getOwner(), subQuestWithCond.getQuestData(), condition, paramStr, params);
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
|
||||
boolean shouldAccept = LogicType.calculate(subQuestWithCond.getQuestData().getAcceptCondComb(), accept);
|
||||
|
||||
if (shouldAccept)
|
||||
subQuestWithCond.start();
|
||||
}
|
||||
this.save();
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void tryFailSubQuests(QuestContent condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond = getChildQuests().values().stream()
|
||||
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNFINISHED)
|
||||
.filter(p -> p.getQuestData().getFailCond().stream().anyMatch(q -> q.getType() == condType))
|
||||
.toList();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
val failCond = subQuestWithCond.getQuestData().getFailCond();
|
||||
|
||||
for (int i = 0; i < subQuestWithCond.getQuestData().getFailCond().size(); i++) {
|
||||
val condition = failCond.get(i);
|
||||
if (condition.getType() == condType) {
|
||||
boolean result = this.getOwner().getServer().getQuestSystem().triggerContent(subQuestWithCond, condition, paramStr, params);
|
||||
subQuestWithCond.getFailProgressList()[i] = result ? 1 : 0;
|
||||
if (result) {
|
||||
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldFail = LogicType.calculate(subQuestWithCond.getQuestData().getFailCondComb(), subQuestWithCond.getFailProgressList());
|
||||
|
||||
if (shouldFail)
|
||||
subQuestWithCond.fail();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred while trying to fail quest.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void tryFinishSubQuests(QuestContent condType, String paramStr, int... params) {
|
||||
try {
|
||||
List<GameQuest> subQuestsWithCond = getChildQuests().values().stream()
|
||||
//There are subQuests with no acceptCond, but can be finished (example: 35104)
|
||||
.filter(p -> p.getState() == QuestState.QUEST_STATE_UNFINISHED && p.getQuestData().getAcceptCond() != null)
|
||||
.filter(p -> p.getQuestData().getFinishCond().stream().anyMatch(q -> q.getType() == condType))
|
||||
.toList();
|
||||
|
||||
for (GameQuest subQuestWithCond : subQuestsWithCond) {
|
||||
val finishCond = subQuestWithCond.getQuestData().getFinishCond();
|
||||
|
||||
for (int i = 0; i < finishCond.size(); i++) {
|
||||
val condition = finishCond.get(i);
|
||||
if (condition.getType() == condType) {
|
||||
boolean result = this.getOwner().getServer().getQuestSystem().triggerContent(subQuestWithCond, condition, paramStr, params);
|
||||
subQuestWithCond.getFinishProgressList()[i] = result ? 1 : 0;
|
||||
if (result) {
|
||||
getOwner().getSession().send(new PacketQuestProgressUpdateNotify(subQuestWithCond));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldFinish = LogicType.calculate(subQuestWithCond.getQuestData().getFinishCondComb(), subQuestWithCond.getFinishProgressList());
|
||||
|
||||
if (shouldFinish)
|
||||
subQuestWithCond.finish();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().debug("An error occurred while trying to finish quest.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
DatabaseHelper.saveQuest(this);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
DatabaseHelper.deleteQuest(this);
|
||||
}
|
||||
|
||||
public ParentQuest toProto(boolean withChildQuests) {
|
||||
var proto = ParentQuest.newBuilder()
|
||||
.setParentQuestId(getParentQuestId())
|
||||
.setIsFinished(isFinished())
|
||||
.setParentQuestState(getState().getValue())
|
||||
.setVideoKey(QuestManager.getQuestKey(parentQuestId));
|
||||
|
||||
if (withChildQuests) {
|
||||
for (var quest : this.getChildQuests().values()) {
|
||||
if (quest.getState() != QuestState.QUEST_STATE_UNSTARTED) {
|
||||
var childQuest = ChildQuest.newBuilder()
|
||||
.setQuestId(quest.getSubQuestId())
|
||||
.setState(quest.getState().getValue())
|
||||
.build();
|
||||
|
||||
proto.addChildQuestList(childQuest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i : getQuestVars()) {
|
||||
proto.addQuestVar(i);
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
|
||||
// TimeVar handling TODO check if ingame or irl time
|
||||
public boolean initTimeVar(int index){
|
||||
if(index >= this.timeVar.length){
|
||||
Grasscutter.getLogger().error("Trying to init out of bounds time var {} for quest {}", index, this.parentQuestId);
|
||||
return false;
|
||||
}
|
||||
this.timeVar[index] = owner.getWorld().getTotalGameTimeMinutes();
|
||||
owner.getActiveQuestTimers().add(this.parentQuestId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean clearTimeVar(int index){
|
||||
if(index >= this.timeVar.length){
|
||||
Grasscutter.getLogger().error("Trying to clear out of bounds time var {} for quest {}", index, this.parentQuestId);
|
||||
return false;
|
||||
}
|
||||
this.timeVar[index] = -1;
|
||||
if(!checkActiveTimers()){
|
||||
owner.getActiveQuestTimers().remove(this.parentQuestId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkActiveTimers(){
|
||||
return Arrays.stream(timeVar).anyMatch(value -> value!=-1);
|
||||
}
|
||||
|
||||
public long getDaysSinceTimeVar(int index){
|
||||
if(index >= this.timeVar.length){
|
||||
Grasscutter.getLogger().error("Trying to get days for out of bounds time var {} for quest {}", index, this.parentQuestId);
|
||||
return -1;
|
||||
}
|
||||
val varTime = timeVar[index];
|
||||
|
||||
if(varTime == -1){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return owner.getWorld().getTotalGameTimeDays() - ConversionUtils.gameTimeToDays(varTime);
|
||||
}
|
||||
|
||||
public long getHoursSinceTimeVar(int index){
|
||||
if(index >= this.timeVar.length){
|
||||
Grasscutter.getLogger().error("Trying to get hours for out of bounds time var {} for quest {}", index, this.parentQuestId);
|
||||
return -1;
|
||||
}
|
||||
val varTime = timeVar[index];
|
||||
|
||||
if(varTime == -1){
|
||||
return 0;
|
||||
}
|
||||
|
||||
return owner.getWorld().getTotalGameTimeDays() - ConversionUtils.gameTimeToDays(varTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,20 +7,25 @@ import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.ChapterData;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.TriggerExcelConfigData;
|
||||
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.net.proto.ChapterStateOuterClass;
|
||||
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.server.packet.send.PacketChapterStateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDelQuestNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.script.Bindings;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@@ -29,12 +34,14 @@ public class GameQuest {
|
||||
|
||||
@Getter private int subQuestId;
|
||||
@Getter private int mainQuestId;
|
||||
@Getter @Setter private QuestState state;
|
||||
@Getter @Setter public QuestState state;
|
||||
|
||||
@Getter @Setter private int startTime;
|
||||
@Getter @Setter private int acceptTime;
|
||||
@Getter @Setter private int finishTime;
|
||||
|
||||
@Getter @Setter private long startGameDay;
|
||||
|
||||
@Getter private int[] finishProgressList;
|
||||
@Getter private int[] failProgressList;
|
||||
@Transient @Getter private Map<String, TriggerExcelConfigData> triggerData;
|
||||
@@ -55,15 +62,17 @@ public class GameQuest {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
clearProgress(false);
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
List<QuestData.QuestCondition> triggerCond =
|
||||
val triggerCond =
|
||||
questData.getFinishCond().stream()
|
||||
.filter(p -> p.getType() == QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.toList();
|
||||
if (triggerCond.size() > 0) {
|
||||
for (QuestData.QuestCondition cond : triggerCond) {
|
||||
for (val cond : triggerCond) {
|
||||
TriggerExcelConfigData newTrigger =
|
||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||
if (newTrigger != null) {
|
||||
@@ -81,21 +90,10 @@ public class GameQuest {
|
||||
}
|
||||
}
|
||||
|
||||
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
|
||||
getQuestData()
|
||||
.getBeginExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||
mainQuest
|
||||
.getOwner()
|
||||
getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||
@@ -104,24 +102,24 @@ public class GameQuest {
|
||||
|
||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||
// different MainQuests)
|
||||
this.getOwner()
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.getSubQuestId(),
|
||||
this.getState().getValue(),
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
getSubQuestId(),
|
||||
getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
this.getOwner()
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_COND_STATE_EQUAL,
|
||||
this.getSubQuestId(),
|
||||
this.getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, getSubQuestId(), getState().getValue(), 0, 0, 0);
|
||||
|
||||
getQuestData()
|
||||
.getBeginExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
getOwner().getQuestManager().checkQuestAlreadyFullfilled(this);
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
||||
}
|
||||
@@ -141,7 +139,7 @@ public class GameQuest {
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (getSubQuestId() != config.getId()) return;
|
||||
if (config == null || getSubQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
@@ -153,11 +151,37 @@ public class GameQuest {
|
||||
failProgressList[index] = value;
|
||||
}
|
||||
|
||||
public boolean clearProgress(boolean notifyDelete) {
|
||||
// TODO improve
|
||||
var oldState = state;
|
||||
if (questData.getFinishCond() != null && questData.getFinishCond().size() != 0) {
|
||||
this.finishProgressList = new int[questData.getFinishCond().size()];
|
||||
}
|
||||
|
||||
if (questData.getFailCond() != null && questData.getFailCond().size() != 0) {
|
||||
this.failProgressList = new int[questData.getFailCond().size()];
|
||||
}
|
||||
setState(QuestState.QUEST_STATE_UNSTARTED);
|
||||
finishTime = 0;
|
||||
acceptTime = 0;
|
||||
startTime = 0;
|
||||
if (oldState == QuestState.QUEST_STATE_UNSTARTED) {
|
||||
return false;
|
||||
}
|
||||
if (notifyDelete) {
|
||||
getOwner().sendPacket(new PacketDelQuestNotify(getSubQuestId()));
|
||||
}
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.state = QuestState.QUEST_STATE_FINISHED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
if (getQuestData().finishParent()) {
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (getQuestData().isFinishParent()) {
|
||||
// This quest finishes the questline - the main quest will also save the quest to db, so we
|
||||
// don't have to call save() here
|
||||
getMainQuest().finish();
|
||||
@@ -169,8 +193,8 @@ public class GameQuest {
|
||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
@@ -178,8 +202,16 @@ public class GameQuest {
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
.queueEvent(QuestContent.QUEST_CONTENT_FINISH_PLOT, this.subQuestId, 0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
getOwner()
|
||||
.getScene()
|
||||
.triggerDungeonEvent(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST, getSubQuestId());
|
||||
|
||||
getOwner().getProgressManager().tryUnlockOpenStates();
|
||||
|
||||
if (ChapterData.endQuestChapterMap.containsKey(subQuestId)) {
|
||||
mainQuest
|
||||
@@ -190,6 +222,10 @@ public class GameQuest {
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
||||
}
|
||||
|
||||
// hard coding to give amber
|
||||
if (getQuestData().getSubId() == 35402) {
|
||||
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
||||
}
|
||||
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
||||
}
|
||||
|
||||
@@ -198,43 +234,47 @@ public class GameQuest {
|
||||
this.state = QuestState.QUEST_STATE_FAILED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
getQuestData()
|
||||
.getFailExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||
getOwner()
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.triggerEvent(
|
||||
QuestTrigger.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
.queueEvent(
|
||||
QuestCond.QUEST_COND_STATE_EQUAL, this.subQuestId, this.state.getValue(), 0, 0, 0);
|
||||
|
||||
this.getQuestData()
|
||||
.getFailExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
|
||||
if (this.getQuestData().getTrialAvatarList() != null) {
|
||||
this.getQuestData()
|
||||
.getTrialAvatarList()
|
||||
.forEach(t -> this.getOwner().getTeamManager().removeTrialAvatar(t));
|
||||
}
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
|
||||
}
|
||||
|
||||
// Return true if ParentQuest should rewind to this childQuest
|
||||
public boolean rewind(GameQuest nextRewind) {
|
||||
if (questData.isRewind()) {
|
||||
if (nextRewind == null) {
|
||||
return true;
|
||||
}
|
||||
// if the next isRewind subQuest is none or unstarted, reset all subQuests with order higher
|
||||
// than this one, and restart this quest
|
||||
if (nextRewind.getState() == QuestState.QUEST_STATE_NONE
|
||||
|| nextRewind.getState() == QuestState.QUEST_STATE_UNSTARTED) {
|
||||
getMainQuest().getChildQuests().values().stream()
|
||||
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
||||
.forEach(q -> q.setState(QuestState.QUEST_STATE_UNSTARTED));
|
||||
this.start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// Return true if it did the rewind
|
||||
public boolean rewind(boolean notifyDelete) {
|
||||
getMainQuest().getChildQuests().values().stream()
|
||||
.filter(p -> p.getQuestData().getOrder() > this.getQuestData().getOrder())
|
||||
.forEach(
|
||||
q -> {
|
||||
q.clearProgress(notifyDelete);
|
||||
});
|
||||
clearProgress(notifyDelete);
|
||||
this.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save() {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class QuestEncryptionKey {
|
||||
int mainQuestId;
|
||||
long encryptionKey;
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class QuestEncryptionKey {
|
||||
int mainQuestId;
|
||||
long encryptionKey;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@Builder(builderMethodName = "of")
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class QuestGroupSuite {
|
||||
int scene;
|
||||
int group;
|
||||
int suite;
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
@Entity
|
||||
@Data
|
||||
@Builder(builderMethodName = "of")
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class QuestGroupSuite {
|
||||
int scene;
|
||||
int group;
|
||||
int suite;
|
||||
}
|
||||
|
||||
@@ -8,34 +8,47 @@ import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.enums.ParentQuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class QuestManager extends BasePlayerManager {
|
||||
|
||||
private static final Set<Integer> newPlayerMainQuests = Set.of(303, 318, 348, 349, 350, 351, 416, 500,
|
||||
501, 502, 503, 504, 505, 506, 507, 508, 509, 20000, 20507, 20509, 21004, 21005, 21010, 21011, 21016, 21017,
|
||||
21020, 21021, 21025, 40063, 70121, 70124, 70511, 71010, 71012, 71013, 71015, 71016, 71017, 71555);
|
||||
@Getter
|
||||
private final Player player;
|
||||
@Getter
|
||||
private final Int2ObjectMap<GameMainQuest> mainQuests;
|
||||
@Getter private final Player player;
|
||||
@Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
|
||||
|
||||
private long lastHourCheck = 0;
|
||||
private long lastDayCheck = 0;
|
||||
|
||||
public static final ExecutorService eventExecutor;
|
||||
static {
|
||||
eventExecutor = new ThreadPoolExecutor(4, 4,
|
||||
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
||||
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
||||
}
|
||||
/*
|
||||
On SetPlayerBornDataReq, the server sends FinishedParentQuestNotify, with this exact
|
||||
parentQuestList. Captured on Game version 2.7
|
||||
Note: quest 40063 is already set to finished, with childQuest 4006406's state set to 3
|
||||
*/
|
||||
@Getter
|
||||
private final List<GameQuest> addToQuestListUpdateNotify;
|
||||
|
||||
private static Set<Integer> newPlayerMainQuests = Set.of(303,318,348,349,350,351,416,500,
|
||||
501,502,503,504,505,506,507,508,509,20000,20507,20509,21004,21005,21010,21011,21016,21017,
|
||||
21020,21021,21025,40063,70121,70124,70511,71010,71012,71013,71015,71016,71017,71555);
|
||||
|
||||
/*
|
||||
On SetPlayerBornDataReq, the server sends ServerCondMeetQuestListUpdateNotify, with this exact
|
||||
@@ -62,74 +75,119 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
*/
|
||||
|
||||
public static long getQuestKey(int mainQuestId) {
|
||||
QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
|
||||
return questEncryptionKey != null ? questEncryptionKey.getEncryptionKey() : 0L;
|
||||
}
|
||||
public QuestManager(Player player) {
|
||||
|
||||
super(player);
|
||||
this.player = player;
|
||||
this.mainQuests = new Int2ObjectOpenHashMap<>();
|
||||
this.addToQuestListUpdateNotify = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static long getQuestKey(int mainQuestId) {
|
||||
QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
|
||||
return questEncryptionKey != null ? questEncryptionKey.getEncryptionKey() : 0L;
|
||||
}
|
||||
|
||||
public void onNewPlayerCreate() {
|
||||
public void onPlayerBorn() {
|
||||
// TODO scan the quest and start the quest with acceptCond fulfilled
|
||||
// The off send 3 request in that order: 1. FinishedParentQuestNotify, 2. QuestListNotify, 3. ServerCondMeetQuestListUpdateNotify
|
||||
|
||||
List<GameMainQuest> newQuests = this.addMultMainQuests(newPlayerMainQuests);
|
||||
//getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
|
||||
for (GameMainQuest mainQuest : newQuests) {
|
||||
startMainQuest(mainQuest.getParentQuestId());
|
||||
}
|
||||
|
||||
//getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
||||
|
||||
|
||||
//getPlayer().sendPacket(new PacketQuestListNotify(subQuests));
|
||||
//getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
|
||||
}
|
||||
|
||||
public void onLogin() {
|
||||
|
||||
List<GameMainQuest> activeQuests = getActiveMainQuests();
|
||||
List<GameQuest> activeSubs = new ArrayList<>(activeQuests.size());
|
||||
for (GameMainQuest quest : activeQuests) {
|
||||
List<Position> rewindPos = quest.rewind(); // <pos, rotation>
|
||||
var activeQuest = quest.getActiveQuests();
|
||||
if (rewindPos != null) {
|
||||
getPlayer().getPosition().set(rewindPos.get(0));
|
||||
getPlayer().getRotation().set(rewindPos.get(1));
|
||||
}
|
||||
if(activeQuest!=null && rewindPos!=null){
|
||||
//activeSubs.add(activeQuest);
|
||||
//player.sendPacket(new PacketQuestProgressUpdateNotify(activeQuest));
|
||||
}
|
||||
quest.checkProgress();
|
||||
}
|
||||
}
|
||||
|
||||
public void onTick(){
|
||||
checkTimeVars();
|
||||
|
||||
// trigger game time tick for quests
|
||||
queueEvent(QuestContent.QUEST_CONTENT_GAME_TIME_TICK,
|
||||
player.getWorld().getGameTimeHours() , // hours
|
||||
0);
|
||||
}
|
||||
|
||||
private void checkTimeVars(){
|
||||
val currentDays = player.getWorld().getTotalGameTimeDays();
|
||||
val currentHours = player.getWorld().getTotalGameTimeHours();
|
||||
boolean checkDays = currentDays != lastDayCheck;
|
||||
boolean checkHours = currentHours != lastHourCheck;
|
||||
|
||||
if(!checkDays && !checkHours){
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastDayCheck = currentDays;
|
||||
this.lastHourCheck = currentHours;
|
||||
player.getActiveQuestTimers().forEach(mainQuestId -> {
|
||||
if(checkHours) {
|
||||
queueEvent(QuestCond.QUEST_COND_TIME_VAR_GT_EQ, mainQuestId);
|
||||
queueEvent(QuestContent.QUEST_CONTENT_TIME_VAR_GT_EQ, mainQuestId);
|
||||
}
|
||||
if(checkDays) {
|
||||
queueEvent(QuestCond.QUEST_COND_TIME_VAR_PASS_DAY, mainQuestId);
|
||||
queueEvent(QuestContent.QUEST_CONTENT_TIME_VAR_PASS_DAY, mainQuestId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private List<GameMainQuest> addMultMainQuests(Set<Integer> mainQuestIds) {
|
||||
List<GameMainQuest> newQuests = new ArrayList<>();
|
||||
for (Integer id : mainQuestIds) {
|
||||
getMainQuests().put(id.intValue(), new GameMainQuest(this.player, id));
|
||||
getMainQuests().put(id.intValue(),new GameMainQuest(this.player, id));
|
||||
getMainQuestById(id).save();
|
||||
newQuests.add(getMainQuestById(id));
|
||||
}
|
||||
return newQuests;
|
||||
}
|
||||
|
||||
public void enableQuests() {
|
||||
onPlayerBorn();
|
||||
}
|
||||
|
||||
/*
|
||||
Looking through mainQuests 72201-72208 and 72174, we can infer that a questGlobalVar's default value is 0
|
||||
*/
|
||||
public Integer getQuestGlobalVarValue(Integer variable) {
|
||||
return getPlayer().getQuestGlobalVariables().getOrDefault(variable, 0);
|
||||
return getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
|
||||
}
|
||||
|
||||
public void setQuestGlobalVarValue(Integer variable, Integer value) {
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().put(variable, value);
|
||||
Grasscutter.getLogger().debug("Changed questGlobalVar {} value from {} to {}", variable, previousValue == null ? 0 : previousValue, value);
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().put(variable,value);
|
||||
Grasscutter.getLogger().debug("Changed questGlobalVar {} value from {} to {}", variable, previousValue==null ? 0: previousValue, value);
|
||||
}
|
||||
|
||||
public void incQuestGlobalVarValue(Integer variable, Integer inc) {
|
||||
//
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable, 0);
|
||||
getPlayer().getQuestGlobalVariables().put(variable, previousValue + inc);
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
|
||||
getPlayer().getQuestGlobalVariables().put(variable,previousValue + inc);
|
||||
Grasscutter.getLogger().debug("Incremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue + inc);
|
||||
}
|
||||
|
||||
//In MainQuest 998, dec is passed as a positive integer
|
||||
public void decQuestGlobalVarValue(Integer variable, Integer dec) {
|
||||
//
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable, 0);
|
||||
getPlayer().getQuestGlobalVariables().put(variable, previousValue - dec);
|
||||
Integer previousValue = getPlayer().getQuestGlobalVariables().getOrDefault(variable,0);
|
||||
getPlayer().getQuestGlobalVariables().put(variable,previousValue - dec);
|
||||
Grasscutter.getLogger().debug("Decremented questGlobalVar {} value from {} to {}", variable, previousValue, previousValue - dec);
|
||||
}
|
||||
|
||||
@@ -137,6 +195,11 @@ public class QuestManager extends BasePlayerManager {
|
||||
return getMainQuests().get(mainQuestId);
|
||||
}
|
||||
|
||||
public GameMainQuest getMainQuestByTalkId(int talkId) {
|
||||
int mainQuestId = GameData.getQuestTalkMap().getOrDefault(talkId, talkId / 100);
|
||||
return getMainQuestById(mainQuestId);
|
||||
}
|
||||
|
||||
public GameQuest getQuestById(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
if (questConfig == null) {
|
||||
@@ -188,6 +251,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
public GameQuest addQuest(int questId) {
|
||||
QuestData questConfig = GameData.getQuestDataMap().get(questId);
|
||||
|
||||
if (questConfig == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -205,14 +269,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
// Forcefully start
|
||||
quest.start();
|
||||
|
||||
// Save main quest
|
||||
mainQuest.save();
|
||||
|
||||
// Send packet
|
||||
getPlayer().sendPacket(new PacketQuestListUpdateNotify(mainQuest.getChildQuests().values().stream()
|
||||
.filter(p -> p.getState() != QuestState.QUEST_STATE_UNSTARTED)
|
||||
.toList()));
|
||||
checkQuestAlreadyFullfilled(quest);
|
||||
|
||||
return quest;
|
||||
}
|
||||
@@ -228,84 +285,79 @@ public class QuestManager extends BasePlayerManager {
|
||||
.min(Comparator.comparingInt(MainQuestData.SubQuestData::getOrder))
|
||||
.map(MainQuestData.SubQuestData::getSubId)
|
||||
.ifPresent(this::addQuest);
|
||||
//TODO find a better way then hardcoding to detect needed required quests
|
||||
if(mainQuestId == 355){
|
||||
startMainQuest(361);
|
||||
startMainQuest(418);
|
||||
startMainQuest(423);
|
||||
startMainQuest(20509);
|
||||
|
||||
}
|
||||
}
|
||||
public void queueEvent(QuestCond condType, int... params) {
|
||||
queueEvent(condType, "", params);
|
||||
}
|
||||
public void queueEvent(QuestContent condType, int... params) {
|
||||
queueEvent(condType, "", params);
|
||||
}
|
||||
|
||||
public void triggerEvent(QuestTrigger condType, int... params) {
|
||||
triggerEvent(condType, "", params);
|
||||
public void queueEvent(QuestContent condType, String paramStr, int... params) {
|
||||
eventExecutor.submit(() -> triggerEvent(condType, paramStr, params));
|
||||
}
|
||||
public void queueEvent(QuestCond condType, String paramStr, int... params) {
|
||||
eventExecutor.submit(() -> triggerEvent(condType, paramStr, params));
|
||||
}
|
||||
|
||||
//TODO
|
||||
public void triggerEvent(QuestTrigger condType, String paramStr, int... params) {
|
||||
//QUEST_EXEC are handled directly by each subQuest
|
||||
|
||||
public void triggerEvent(QuestCond condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
|
||||
.toList();
|
||||
switch (condType) {
|
||||
//accept Conds
|
||||
case QUEST_COND_STATE_EQUAL:
|
||||
case QUEST_COND_STATE_NOT_EQUAL:
|
||||
case QUEST_COND_COMPLETE_TALK:
|
||||
case QUEST_COND_LUA_NOTIFY:
|
||||
case QUEST_COND_QUEST_VAR_EQUAL:
|
||||
case QUEST_COND_QUEST_VAR_GREATER:
|
||||
case QUEST_COND_QUEST_VAR_LESS:
|
||||
case QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER:
|
||||
case QUEST_COND_QUEST_GLOBAL_VAR_EQUAL:
|
||||
case QUEST_COND_QUEST_GLOBAL_VAR_GREATER:
|
||||
case QUEST_COND_QUEST_GLOBAL_VAR_LESS:
|
||||
for (GameMainQuest mainquest : checkMainQuests) {
|
||||
mainquest.tryAcceptSubQuests(condType, paramStr, params);
|
||||
}
|
||||
break;
|
||||
|
||||
//fail Conds
|
||||
case QUEST_CONTENT_NOT_FINISH_PLOT:
|
||||
for (GameMainQuest mainquest : checkMainQuests) {
|
||||
mainquest.tryFailSubQuests(condType, paramStr, params);
|
||||
}
|
||||
break;
|
||||
//finish Conds
|
||||
case QUEST_CONTENT_COMPLETE_TALK:
|
||||
case QUEST_CONTENT_FINISH_PLOT:
|
||||
case QUEST_CONTENT_COMPLETE_ANY_TALK:
|
||||
case QUEST_CONTENT_LUA_NOTIFY:
|
||||
case QUEST_CONTENT_QUEST_VAR_EQUAL:
|
||||
case QUEST_CONTENT_QUEST_VAR_GREATER:
|
||||
case QUEST_CONTENT_QUEST_VAR_LESS:
|
||||
case QUEST_CONTENT_ENTER_DUNGEON:
|
||||
case QUEST_CONTENT_ENTER_ROOM:
|
||||
case QUEST_CONTENT_INTERACT_GADGET:
|
||||
case QUEST_CONTENT_TRIGGER_FIRE:
|
||||
case QUEST_CONTENT_UNLOCK_TRANS_POINT:
|
||||
case QUEST_CONTENT_SKILL:
|
||||
for (GameMainQuest mainQuest : checkMainQuests) {
|
||||
mainQuest.tryFinishSubQuests(condType, paramStr, params);
|
||||
}
|
||||
break;
|
||||
|
||||
//finish Or Fail Conds
|
||||
case QUEST_CONTENT_GAME_TIME_TICK:
|
||||
case QUEST_CONTENT_QUEST_STATE_EQUAL:
|
||||
case QUEST_CONTENT_ADD_QUEST_PROGRESS:
|
||||
case QUEST_CONTENT_LEAVE_SCENE:
|
||||
for (GameMainQuest mainQuest : checkMainQuests) {
|
||||
mainQuest.tryFailSubQuests(condType, paramStr, params);
|
||||
mainQuest.tryFinishSubQuests(condType, paramStr, params);
|
||||
}
|
||||
break;
|
||||
//QUEST_EXEC are handled directly by each subQuest
|
||||
|
||||
//Unused
|
||||
case QUEST_CONTENT_QUEST_STATE_NOT_EQUAL:
|
||||
case QUEST_COND_PLAYER_CHOOSE_MALE:
|
||||
default:
|
||||
Grasscutter.getLogger().error("Unhandled QuestTrigger {}", condType);
|
||||
for (GameMainQuest mainquest : checkMainQuests) {
|
||||
mainquest.tryAcceptSubQuests(condType, paramStr, params);
|
||||
}
|
||||
if (this.addToQuestListUpdateNotify.size() != 0) {
|
||||
this.getPlayer().getSession().send(new PacketQuestListUpdateNotify(this.addToQuestListUpdateNotify));
|
||||
this.addToQuestListUpdateNotify.clear();
|
||||
}
|
||||
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||
.filter(i -> i.getState() != ParentQuestState.PARENT_QUEST_STATE_FINISHED)
|
||||
.toList();
|
||||
for (GameMainQuest mainQuest : checkMainQuests) {
|
||||
mainQuest.tryFailSubQuests(condType, paramStr, params);
|
||||
mainQuest.tryFinishSubQuests(condType, paramStr, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO maybe trigger them delayed to allow basic communication finish first
|
||||
* @param quest
|
||||
*/
|
||||
public void checkQuestAlreadyFullfilled(GameQuest quest){
|
||||
Grasscutter.getGameServer().getScheduler().scheduleDelayedTask(() -> {
|
||||
for(var condition : quest.getQuestData().getFinishCond()){
|
||||
switch (condition.getType()) {
|
||||
case QUEST_CONTENT_OBTAIN_ITEM, QUEST_CONTENT_ITEM_LESS_THAN -> {
|
||||
//check if we already own enough of the item
|
||||
var item = getPlayer().getInventory().getItemByGuid(condition.getParam()[0]);
|
||||
queueEvent(condition.getType(), condition.getParam()[0], item != null ? item.getCount() : 0);
|
||||
}
|
||||
case QUEST_CONTENT_UNLOCK_TRANS_POINT -> {
|
||||
var scenePoints = getPlayer().getUnlockedScenePoints().get(condition.getParam()[0]);
|
||||
if (scenePoints != null && scenePoints.contains(condition.getParam()[1])) {
|
||||
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
||||
}
|
||||
}
|
||||
case QUEST_CONTENT_UNLOCK_AREA -> {
|
||||
var sceneAreas = getPlayer().getUnlockedSceneAreas().get(condition.getParam()[0]);
|
||||
if (sceneAreas != null && sceneAreas.contains(condition.getParam()[1])) {
|
||||
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
public List<QuestGroupSuite> getSceneGroupSuite(int sceneId) {
|
||||
@@ -317,7 +369,6 @@ public class QuestManager extends BasePlayerManager {
|
||||
.filter(i -> i.getScene() == sceneId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void loadFromDatabase() {
|
||||
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.QuestData.QuestCondition;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.data.excels.QuestData.QuestAcceptCondition;
|
||||
import emu.grasscutter.data.excels.QuestData.QuestContentCondition;
|
||||
import emu.grasscutter.data.excels.QuestData.QuestExecParam;
|
||||
import emu.grasscutter.game.quest.handlers.QuestBaseHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.conditions.BaseCondition;
|
||||
import emu.grasscutter.game.quest.content.BaseContent;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
@@ -13,8 +17,8 @@ import org.reflections.Reflections;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class QuestSystem extends BaseGameSystem {
|
||||
private final Int2ObjectMap<QuestBaseHandler> condHandlers;
|
||||
private final Int2ObjectMap<QuestBaseHandler> contHandlers;
|
||||
private final Int2ObjectMap<BaseCondition> condHandlers;
|
||||
private final Int2ObjectMap<BaseContent> contHandlers;
|
||||
private final Int2ObjectMap<QuestExecHandler> execHandlers;
|
||||
|
||||
public QuestSystem(GameServer server) {
|
||||
@@ -29,9 +33,9 @@ public class QuestSystem extends BaseGameSystem {
|
||||
|
||||
public void registerHandlers() {
|
||||
this.registerHandlers(
|
||||
this.condHandlers, "emu.grasscutter.game.quest.conditions", QuestBaseHandler.class);
|
||||
this.condHandlers, "emu.grasscutter.game.quest.conditions", BaseCondition.class);
|
||||
this.registerHandlers(
|
||||
this.contHandlers, "emu.grasscutter.game.quest.content", QuestBaseHandler.class);
|
||||
this.contHandlers, "emu.grasscutter.game.quest.content", BaseContent.class);
|
||||
this.registerHandlers(
|
||||
this.execHandlers, "emu.grasscutter.game.quest.exec", QuestExecHandler.class);
|
||||
}
|
||||
@@ -47,13 +51,25 @@ public class QuestSystem extends BaseGameSystem {
|
||||
|
||||
public <T> void registerPacketHandler(Int2ObjectMap<T> map, Class<? extends T> handlerClass) {
|
||||
try {
|
||||
QuestValue opcode = handlerClass.getAnnotation(QuestValue.class);
|
||||
|
||||
if (opcode == null || opcode.value().getValue() <= 0) {
|
||||
int value = 0;
|
||||
if (handlerClass.isAnnotationPresent(QuestValueExec.class)) {
|
||||
QuestValueExec opcode = handlerClass.getAnnotation(QuestValueExec.class);
|
||||
value = opcode.value().getValue();
|
||||
} else if (handlerClass.isAnnotationPresent(QuestValueContent.class)) {
|
||||
QuestValueContent opcode = handlerClass.getAnnotation(QuestValueContent.class);
|
||||
value = opcode.value().getValue();
|
||||
} else if (handlerClass.isAnnotationPresent(QuestValueCond.class)) {
|
||||
QuestValueCond opcode = handlerClass.getAnnotation(QuestValueCond.class);
|
||||
value = opcode.value().getValue();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
map.put(opcode.value().getValue(), handlerClass.getDeclaredConstructor().newInstance());
|
||||
if (value <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
map.put(value, handlerClass.getDeclaredConstructor().newInstance());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -62,24 +78,25 @@ public class QuestSystem extends BaseGameSystem {
|
||||
// TODO make cleaner
|
||||
|
||||
public boolean triggerCondition(
|
||||
GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = condHandlers.get(condition.getType().getValue());
|
||||
Player owner,
|
||||
QuestData questData,
|
||||
QuestAcceptCondition condition,
|
||||
String paramStr,
|
||||
int... params) {
|
||||
BaseCondition handler = condHandlers.get(condition.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getQuestData() == null) {
|
||||
if (handler == null || questData == null) {
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"Could not trigger condition {} at {}",
|
||||
condition.getType().getValue(),
|
||||
quest.getQuestData());
|
||||
.debug("Could not trigger condition {} at {}", condition.getType().getValue(), questData);
|
||||
return false;
|
||||
}
|
||||
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
return handler.execute(owner, questData, condition, paramStr, params);
|
||||
}
|
||||
|
||||
public boolean triggerContent(
|
||||
GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
QuestBaseHandler handler = contHandlers.get(condition.getType().getValue());
|
||||
GameQuest quest, QuestContentCondition condition, String paramStr, int... params) {
|
||||
BaseContent handler = contHandlers.get(condition.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getQuestData() == null) {
|
||||
Grasscutter.getLogger()
|
||||
@@ -93,7 +110,7 @@ public class QuestSystem extends BaseGameSystem {
|
||||
return handler.execute(quest, condition, paramStr, params);
|
||||
}
|
||||
|
||||
public boolean triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
|
||||
public void triggerExec(GameQuest quest, QuestExecParam execParam, String... params) {
|
||||
QuestExecHandler handler = execHandlers.get(execParam.getType().getValue());
|
||||
|
||||
if (handler == null || quest.getQuestData() == null) {
|
||||
@@ -102,9 +119,18 @@ public class QuestSystem extends BaseGameSystem {
|
||||
"Could not trigger exec {} at {}",
|
||||
execParam.getType().getValue(),
|
||||
quest.getQuestData());
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
return handler.execute(quest, execParam, params);
|
||||
QuestManager.eventExecutor.submit(
|
||||
() -> {
|
||||
if (!handler.execute(quest, execParam, params)) {
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"exec trigger failed {} at {}",
|
||||
execParam.getType().getValue(),
|
||||
quest.getQuestData());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueCond {
|
||||
QuestCond value();
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueCond {
|
||||
QuestCond value();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueContent {
|
||||
QuestContent value();
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueContent {
|
||||
QuestContent value();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueExec {
|
||||
QuestExec value();
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface QuestValueExec {
|
||||
QuestExec value();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class RewindData {
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class Npc {
|
||||
public static class Npc {
|
||||
private String script;
|
||||
private int room_id;
|
||||
private int data_index;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TeleportData {
|
||||
List<TransmitPoint> transmit_points;
|
||||
List<Npc> npcs;
|
||||
List<Gadget> gadgets;
|
||||
|
||||
@Data
|
||||
public static class TransmitPoint {
|
||||
private int point_id;
|
||||
private int scene_id;
|
||||
private String pos;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Npc {
|
||||
private int data_index;
|
||||
private int room_id;
|
||||
private int scene_id;
|
||||
private int id;
|
||||
private String alias;
|
||||
private String script;
|
||||
private String pos;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Gadget {
|
||||
private int id;
|
||||
private String pos;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TeleportData {
|
||||
List<TransmitPoint> transmit_points;
|
||||
List<Npc> npcs;
|
||||
List<Gadget> gadgets;
|
||||
|
||||
@Data
|
||||
public static class TransmitPoint {
|
||||
private int point_id;
|
||||
private int scene_id;
|
||||
private String pos;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Npc {
|
||||
private int data_index;
|
||||
private int room_id;
|
||||
private int scene_id;
|
||||
private int id;
|
||||
private String alias;
|
||||
private String script;
|
||||
private String pos;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Gadget {
|
||||
private int id;
|
||||
private String pos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package emu.grasscutter.game.quest.conditions;
|
||||
|
||||
import emu.grasscutter.data.excels.QuestData.QuestCondition;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValue;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.game.quest.handlers.QuestBaseHandler;
|
||||
import static emu.grasscutter.game.quest.enums.QuestCond.QUEST_COND_UNKNOWN;
|
||||
|
||||
@QuestValue(QuestTrigger.QUEST_CONTENT_NONE)
|
||||
public class BaseCondition extends QuestBaseHandler {
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.quest.QuestValueCond;
|
||||
|
||||
@QuestValueCond(QUEST_COND_UNKNOWN)
|
||||
public class BaseCondition {
|
||||
|
||||
@Override
|
||||
public boolean execute(
|
||||
GameQuest quest, QuestCondition condition, String paramStr, int... params) {
|
||||
// TODO Auto-generated method stub
|
||||
Player owner,
|
||||
QuestData questData,
|
||||
QuestData.QuestAcceptCondition condition,
|
||||
String paramStr,
|
||||
int... params) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user