mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-20 02:45:52 +01:00
Continue merging quests (pt. 2)
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
|
||||
public class InTimeTrigger extends ChallengeTrigger {
|
||||
@Override
|
||||
public void onCheckTimeout(WorldChallenge challenge) {
|
||||
var current = System.currentTimeMillis();
|
||||
if (current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L) {
|
||||
challenge.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
|
||||
public class InTimeTrigger extends ChallengeTrigger {
|
||||
@Override
|
||||
public void onCheckTimeout(WorldChallenge challenge) {
|
||||
var current = challenge.getScene().getSceneTimeSeconds();
|
||||
if (current - challenge.getStartedAt() > challenge.getTimeLimit()) {
|
||||
challenge.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import emu.grasscutter.game.activity.ActivityManager;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.battlepass.BattlePassManager;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.expedition.ExpeditionInfo;
|
||||
import emu.grasscutter.game.friends.FriendsList;
|
||||
@@ -44,6 +45,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.QuestCond;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.shop.ShopLimit;
|
||||
import emu.grasscutter.game.tower.TowerData;
|
||||
@@ -51,20 +53,18 @@ import emu.grasscutter.game.tower.TowerManager;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
|
||||
import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo;
|
||||
import emu.grasscutter.net.proto.PlayerWorldLocationInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture;
|
||||
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
|
||||
import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
|
||||
import emu.grasscutter.net.proto.SocialShowAvatarInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||
import emu.grasscutter.scripts.data.SceneRegion;
|
||||
import emu.grasscutter.server.event.player.PlayerJoinEvent;
|
||||
import emu.grasscutter.server.event.player.PlayerQuitEvent;
|
||||
@@ -88,6 +88,7 @@ import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
@@ -472,6 +473,8 @@ public class Player {
|
||||
|
||||
// Handle open state unlocks from level-up.
|
||||
this.getProgressManager().tryUnlockOpenStates();
|
||||
this.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_PLAYER_LEVEL_UP, level);
|
||||
this.getQuestManager().queueEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, level);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -613,7 +616,7 @@ public class Player {
|
||||
|
||||
public void onEnterRegion(SceneRegion region) {
|
||||
getQuestManager().forEachActiveQuest(quest -> {
|
||||
if (quest.getTriggers().containsKey("ENTER_REGION_" + region.config_id)) {
|
||||
if (quest.getTriggerData() != null && quest.getTriggers().containsKey("ENTER_REGION_"+ region.config_id)) {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
||||
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
@@ -824,6 +827,85 @@ public class Player {
|
||||
addAvatar(avatar, true);
|
||||
}
|
||||
|
||||
public void addAvatar(int avatarId) {
|
||||
// I dont see why we cant do this lolz
|
||||
addAvatar(new Avatar(avatarId), true);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
var trialCustomParams = GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
|
||||
return trialCustomParams.isEmpty() ? List.of() : Stream.of(trialCustomParams.get(0).split(";")).map(Integer::parseInt).toList();
|
||||
}
|
||||
|
||||
public boolean addTrialAvatar(int trialAvatarId, GrantReason reason, int questMainId){
|
||||
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(trialAvatarId);
|
||||
if (trialAvatarBasicParam.isEmpty()) return false;
|
||||
|
||||
Avatar avatar = new Avatar(trialAvatarBasicParam.get(0));
|
||||
if (avatar.getAvatarData() == null || !hasSentLoginPackets()) return false;
|
||||
|
||||
avatar.setOwner(this);
|
||||
// Add trial weapons and relics
|
||||
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), trialAvatarId, reason, questMainId);
|
||||
avatar.equipTrialItems();
|
||||
// Recalc stats
|
||||
avatar.recalcStats();
|
||||
|
||||
// Packet, mimic official server behaviour, add to player's bag but not saving to db
|
||||
sendPacket(new PacketAvatarAddNotify(avatar, false));
|
||||
// add to avatar to temporary trial team
|
||||
getTeamManager().addAvatarToTrialTeam(avatar);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addTrialAvatarForQuest(int trialAvatarId, int questMainId) {
|
||||
// TODO: Find method for 'setupTrialAvatarTeamForQuest'.
|
||||
getTeamManager().setupTrialAvatars(true);
|
||||
if (!addTrialAvatar(
|
||||
trialAvatarId,
|
||||
GrantReason.GRANT_REASON_BY_QUEST,
|
||||
questMainId)) return false;
|
||||
getTeamManager().trialAvatarTeamPostUpdate();
|
||||
// Packet, mimic official server behaviour, neccessary to stop player from modifying team
|
||||
sendPacket(new PacketAvatarTeamUpdateNotify(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void addTrialAvatarsForActivity(List<Integer> trialAvatarIds) {
|
||||
getTeamManager().setupTrialAvatars(false);
|
||||
trialAvatarIds.forEach(trialAvatarId -> addTrialAvatar(
|
||||
trialAvatarId,
|
||||
GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY,
|
||||
0));
|
||||
getTeamManager().trialAvatarTeamPostUpdate(0);
|
||||
}
|
||||
|
||||
public boolean removeTrialAvatarForQuest(int trialAvatarId) {
|
||||
if (!getTeamManager().isUsingTrialTeam()) return false;
|
||||
|
||||
sendPacket(new PacketAvatarDelNotify(List.of(getTeamManager().getTrialAvatarGuid(trialAvatarId))));
|
||||
getTeamManager().removeTrialAvatarTeam(trialAvatarId);
|
||||
sendPacket(new PacketAvatarTeamUpdateNotify());
|
||||
return true;
|
||||
}
|
||||
|
||||
public void removeTrialAvatarForActivity() {
|
||||
if (!getTeamManager().isUsingTrialTeam()) return;
|
||||
|
||||
sendPacket(new PacketAvatarDelNotify(getTeamManager().getActiveTeam().stream()
|
||||
.map(x -> x.getAvatar().getGuid()).toList()));
|
||||
getTeamManager().removeTrialAvatarTeam();
|
||||
}
|
||||
|
||||
public void addFlycloak(int flycloakId) {
|
||||
this.getFlyCloakList().add(flycloakId);
|
||||
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
|
||||
@@ -834,6 +916,11 @@ public class Player {
|
||||
this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
|
||||
}
|
||||
|
||||
public void addPersonalLine(int personalLineId) {
|
||||
this.getPersonalLineList().add(personalLineId);
|
||||
session.getPlayer().getQuestManager().queueEvent(QuestCond.QUEST_COND_PERSONAL_LINE_UNLOCK, personalLineId);
|
||||
}
|
||||
|
||||
public void addNameCard(int nameCardId) {
|
||||
this.getNameCardList().add(nameCardId);
|
||||
this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
|
||||
@@ -863,6 +950,46 @@ public class Player {
|
||||
this.getServer().getChatSystem().sendPrivateMessageFromServer(getUid(), message.toString());
|
||||
}
|
||||
|
||||
public void setAvatarsAbilityForScene(Scene scene) {
|
||||
try {
|
||||
var levelEntityConfig = scene.getSceneData().getLevelEntityConfig();
|
||||
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
|
||||
if (config == null){
|
||||
return;
|
||||
}
|
||||
|
||||
List<Integer> avatarIds = scene.getSceneData().getSpecifiedAvatarList();
|
||||
List<EntityAvatar> specifiedAvatarList = getTeamManager().getActiveTeam();
|
||||
|
||||
if (avatarIds != null && avatarIds.size() > 0){
|
||||
// certain scene could limit specifc avatars' entry
|
||||
specifiedAvatarList.clear();
|
||||
for (int id : avatarIds){
|
||||
var avatar = getAvatars().getAvatarById(id);
|
||||
if (avatar == null){
|
||||
continue;
|
||||
}
|
||||
specifiedAvatarList.add(new EntityAvatar(scene, avatar));
|
||||
}
|
||||
}
|
||||
|
||||
for (EntityAvatar entityAvatar : specifiedAvatarList){
|
||||
var avatarData = entityAvatar.getAvatar().getAvatarData();
|
||||
if (avatarData == null){
|
||||
continue;
|
||||
}
|
||||
avatarData.buildEmbryo();
|
||||
if (config.getAvatarAbilities() == null){
|
||||
continue; // continue and not break because has to rebuild ability for the next avatar if any
|
||||
}
|
||||
for (var 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);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends a message to another player.
|
||||
*
|
||||
@@ -982,7 +1109,7 @@ public class Player {
|
||||
}
|
||||
}
|
||||
|
||||
SocialDetail.Builder social = SocialDetail.newBuilder()
|
||||
return SocialDetail.newBuilder()
|
||||
.setUid(this.getUid())
|
||||
.setProfilePicture(ProfilePicture.newBuilder().setAvatarId(this.getHeadImage()))
|
||||
.setNickname(this.getNickname())
|
||||
@@ -996,7 +1123,6 @@ public class Player {
|
||||
.addAllShowNameCardIdList(this.getShowNameCardInfoList())
|
||||
.setFinishAchievementNum(this.getFinishedAchievementNum())
|
||||
.setFriendEnterHomeOptionValue(this.getHome() == null ? 0 : this.getHome().getEnterHomeOption());
|
||||
return social;
|
||||
}
|
||||
|
||||
public int getFinishedAchievementNum() {
|
||||
@@ -1135,6 +1261,8 @@ public class Player {
|
||||
|
||||
// Home resources
|
||||
this.getHome().updateHourlyResources(this);
|
||||
|
||||
this.getQuestManager().onTick();
|
||||
}
|
||||
|
||||
private synchronized void doDailyReset() {
|
||||
@@ -1200,12 +1328,17 @@ public class Player {
|
||||
this.achievements = Achievements.getByPlayer(this);
|
||||
this.getAvatars().loadFromDatabase();
|
||||
this.getInventory().loadFromDatabase();
|
||||
this.loadBattlePassManager(); // Call before avatar postLoad to avoid null pointer
|
||||
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
||||
|
||||
this.getFriendsList().loadFromDatabase();
|
||||
this.getMailHandler().loadFromDatabase();
|
||||
this.getQuestManager().loadFromDatabase();
|
||||
|
||||
this.loadBattlePassManager();
|
||||
this.getAvatars().postLoad(); // Needs to be called after inventory is handled
|
||||
}
|
||||
|
||||
public void onPlayerBorn() {
|
||||
getQuestManager().onPlayerBorn();
|
||||
}
|
||||
|
||||
public void onLogin() {
|
||||
|
||||
@@ -1,108 +1,109 @@
|
||||
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 EntityType {
|
||||
None(0),
|
||||
Avatar(1),
|
||||
Monster(2),
|
||||
Bullet(3),
|
||||
AttackPhyisicalUnit(4),
|
||||
AOE(5),
|
||||
Camera(6),
|
||||
EnviroArea(7),
|
||||
Equip(8),
|
||||
MonsterEquip(9),
|
||||
Grass(10),
|
||||
Level(11),
|
||||
NPC(12),
|
||||
TransPointFirst(13),
|
||||
TransPointFirstGadget(14),
|
||||
TransPointSecond(15),
|
||||
TransPointSecondGadget(16),
|
||||
DropItem(17),
|
||||
Field(18),
|
||||
Gadget(19),
|
||||
Water(20),
|
||||
GatherPoint(21),
|
||||
GatherObject(22),
|
||||
AirflowField(23),
|
||||
SpeedupField(24),
|
||||
Gear(25),
|
||||
Chest(26),
|
||||
EnergyBall(27),
|
||||
ElemCrystal(28),
|
||||
Timeline(29),
|
||||
Worktop(30),
|
||||
Team(31),
|
||||
Platform(32),
|
||||
AmberWind(33),
|
||||
EnvAnimal(34),
|
||||
SealGadget(35),
|
||||
Tree(36),
|
||||
Bush(37),
|
||||
QuestGadget(38),
|
||||
Lightning(39),
|
||||
RewardPoint(40),
|
||||
RewardStatue(41),
|
||||
MPLevel(42),
|
||||
WindSeed(43),
|
||||
MpPlayRewardPoint(44),
|
||||
ViewPoint(45),
|
||||
RemoteAvatar(46),
|
||||
GeneralRewardPoint(47),
|
||||
PlayTeam(48),
|
||||
OfferingGadget(49),
|
||||
EyePoint(50),
|
||||
MiracleRing(51),
|
||||
Foundation(52),
|
||||
WidgetGadget(53),
|
||||
Vehicle(54),
|
||||
SubEquip(55),
|
||||
FishRod(56),
|
||||
CustomTile(57),
|
||||
FishPool(58),
|
||||
CustomGadget(59),
|
||||
BlackMud(60),
|
||||
RoguelikeOperatorGadget(61),
|
||||
NightCrowGadget(62),
|
||||
Projector(63),
|
||||
Screen(64),
|
||||
EchoShell(65),
|
||||
UIInteractGadget(66),
|
||||
PlaceHolder(99);
|
||||
|
||||
private static final Int2ObjectMap<EntityType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, EntityType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values())
|
||||
.forEach(
|
||||
e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private final int value;
|
||||
|
||||
EntityType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static EntityType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, None);
|
||||
}
|
||||
|
||||
public static EntityType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, None);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.props;
|
||||
|
||||
import emu.grasscutter.scripts.constants.IntValueEnum;
|
||||
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 EntityType implements IntValueEnum {
|
||||
None(0),
|
||||
Avatar(1),
|
||||
Monster(2),
|
||||
Bullet(3),
|
||||
AttackPhyisicalUnit(4),
|
||||
AOE(5),
|
||||
Camera(6),
|
||||
EnviroArea(7),
|
||||
Equip(8),
|
||||
MonsterEquip(9),
|
||||
Grass(10),
|
||||
Level(11),
|
||||
NPC(12),
|
||||
TransPointFirst(13),
|
||||
TransPointFirstGadget(14),
|
||||
TransPointSecond(15),
|
||||
TransPointSecondGadget(16),
|
||||
DropItem(17),
|
||||
Field(18),
|
||||
Gadget(19),
|
||||
Water(20),
|
||||
GatherPoint(21),
|
||||
GatherObject(22),
|
||||
AirflowField(23),
|
||||
SpeedupField(24),
|
||||
Gear(25),
|
||||
Chest(26),
|
||||
EnergyBall(27),
|
||||
ElemCrystal(28),
|
||||
Timeline(29),
|
||||
Worktop(30),
|
||||
Team(31),
|
||||
Platform(32),
|
||||
AmberWind(33),
|
||||
EnvAnimal(34),
|
||||
SealGadget(35),
|
||||
Tree(36),
|
||||
Bush(37),
|
||||
QuestGadget(38),
|
||||
Lightning(39),
|
||||
RewardPoint(40),
|
||||
RewardStatue(41),
|
||||
MPLevel(42),
|
||||
WindSeed(43),
|
||||
MpPlayRewardPoint(44),
|
||||
ViewPoint(45),
|
||||
RemoteAvatar(46),
|
||||
GeneralRewardPoint(47),
|
||||
PlayTeam(48),
|
||||
OfferingGadget(49),
|
||||
EyePoint(50),
|
||||
MiracleRing(51),
|
||||
Foundation(52),
|
||||
WidgetGadget(53),
|
||||
Vehicle(54),
|
||||
SubEquip(55),
|
||||
FishRod(56),
|
||||
CustomTile(57),
|
||||
FishPool(58),
|
||||
CustomGadget(59),
|
||||
BlackMud(60),
|
||||
RoguelikeOperatorGadget(61),
|
||||
NightCrowGadget(62),
|
||||
Projector(63),
|
||||
Screen(64),
|
||||
EchoShell(65),
|
||||
UIInteractGadget(66),
|
||||
PlaceHolder(99);
|
||||
|
||||
private static final Int2ObjectMap<EntityType> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, EntityType> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(values())
|
||||
.forEach(
|
||||
e -> {
|
||||
map.put(e.getValue(), e);
|
||||
stringMap.put(e.name(), e);
|
||||
});
|
||||
}
|
||||
|
||||
private final int value;
|
||||
|
||||
EntityType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static EntityType getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, None);
|
||||
}
|
||||
|
||||
public static EntityType getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, None);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ 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;
|
||||
@@ -41,7 +40,7 @@ public class GameMainQuest {
|
||||
@Getter private int[] questVars;
|
||||
@Getter private long[] timeVar;
|
||||
//QuestUpdateQuestVarReq is sent in two stages...
|
||||
@Getter private List<Integer> questVarsUpdate;
|
||||
private List<Integer> questVarsUpdate;
|
||||
@Getter private ParentQuestState state;
|
||||
@Getter private boolean isFinished;
|
||||
@Getter List<QuestGroupSuite> questGroupSuites;
|
||||
@@ -67,6 +66,13 @@ public class GameMainQuest {
|
||||
addAllChildQuests();
|
||||
}
|
||||
|
||||
public List<Integer> getQuestVarsUpdate() {
|
||||
if(questVarsUpdate == null){
|
||||
questVarsUpdate = new ArrayList<>();
|
||||
}
|
||||
return questVarsUpdate;
|
||||
}
|
||||
|
||||
private void addAllChildQuests() {
|
||||
List<Integer> subQuestIds = Arrays.stream(GameData.getMainQuestDataMap().get(this.parentQuestId).getSubQuests()).map(SubQuestData::getSubId).toList();
|
||||
for (Integer subQuestId : subQuestIds) {
|
||||
@@ -166,10 +172,10 @@ public class GameMainQuest {
|
||||
}
|
||||
|
||||
// handoff main quest
|
||||
if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
.forEach(getQuestManager()::startMainQuest);
|
||||
}
|
||||
// if (mainQuestData.getSuggestTrackMainQuestList() != null) {
|
||||
// Arrays.stream(mainQuestData.getSuggestTrackMainQuestList())
|
||||
// .forEach(getQuestManager()::startMainQuest);
|
||||
// }
|
||||
}
|
||||
//TODO
|
||||
public void fail() {}
|
||||
@@ -181,9 +187,9 @@ public class GameMainQuest {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*if(rewindPositions.isEmpty()){
|
||||
addRewindPoints();
|
||||
}*/
|
||||
// if(rewindPositions.isEmpty()){
|
||||
// this.addRewindPoints();
|
||||
// }
|
||||
|
||||
List<Position> posAndRot = new ArrayList<>();
|
||||
if(hasRewindPosition(targetQuest.getSubQuestId(), posAndRot)){
|
||||
@@ -198,8 +204,8 @@ public class GameMainQuest {
|
||||
if (hasRewindPosition(quest.getSubQuestId(), posAndRot)) {
|
||||
return posAndRot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,308 +1,312 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
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.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.Map;
|
||||
import javax.script.Bindings;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||
@Transient @Getter private QuestData questData;
|
||||
|
||||
@Getter private int subQuestId;
|
||||
@Getter private int mainQuestId;
|
||||
@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;
|
||||
@Getter private Map<String, Boolean> triggers;
|
||||
private transient Bindings bindings;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.subQuestId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
||||
this.triggerData = new HashMap<>();
|
||||
this.triggers = new HashMap<>();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
clearProgress(false);
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
val triggerCond =
|
||||
questData.getFinishCond().stream()
|
||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.toList();
|
||||
if (triggerCond.size() > 0) {
|
||||
for (val cond : triggerCond) {
|
||||
TriggerExcelConfigData newTrigger =
|
||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||
if (newTrigger != null) {
|
||||
if (this.triggerData == null) {
|
||||
this.triggerData = new HashMap<>();
|
||||
}
|
||||
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
||||
triggers.put(newTrigger.getTriggerName(), false);
|
||||
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
||||
getOwner()
|
||||
.getWorld()
|
||||
.getSceneById(newTrigger.getSceneId())
|
||||
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||
getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
||||
}
|
||||
|
||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||
// different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
getSubQuestId(),
|
||||
getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.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);
|
||||
}
|
||||
|
||||
public String getTriggerNameById(int id) {
|
||||
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
||||
if (trigger != null) {
|
||||
String triggerName = trigger.getTriggerName();
|
||||
return triggerName;
|
||||
}
|
||||
// return empty string if can't find trigger
|
||||
return "";
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return this.getMainQuest().getOwner();
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (config == null || getSubQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
|
||||
public void setFailProgress(int index, int value) {
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
getQuestData()
|
||||
.getFinishExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.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
|
||||
.getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void fail() {
|
||||
this.state = QuestState.QUEST_STATE_FAILED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.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 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() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto =
|
||||
Quest.newBuilder()
|
||||
.setQuestId(getSubQuestId())
|
||||
.setState(getState().getValue())
|
||||
.setParentQuestId(getMainQuestId())
|
||||
.setStartTime(getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(getAcceptTime());
|
||||
|
||||
if (getFinishProgressList() != null) {
|
||||
for (int i : getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (getFailProgressList() != null) {
|
||||
for (int i : getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
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.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.Map;
|
||||
import javax.script.Bindings;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||
@Transient @Getter private QuestData questData;
|
||||
|
||||
@Getter private int subQuestId;
|
||||
@Getter private int mainQuestId;
|
||||
@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;
|
||||
@Getter private Map<String, Boolean> triggers;
|
||||
private transient Bindings bindings;
|
||||
|
||||
@Deprecated // Morphia only. Do not use.
|
||||
public GameQuest() {}
|
||||
|
||||
public GameQuest(GameMainQuest mainQuest, QuestData questData) {
|
||||
this.mainQuest = mainQuest;
|
||||
this.subQuestId = questData.getId();
|
||||
this.mainQuestId = questData.getMainId();
|
||||
this.questData = questData;
|
||||
this.state = QuestState.QUEST_STATE_UNSTARTED;
|
||||
this.triggerData = new HashMap<>();
|
||||
this.triggers = new HashMap<>();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
clearProgress(false);
|
||||
this.acceptTime = Utils.getCurrentSeconds();
|
||||
this.startTime = this.acceptTime;
|
||||
this.startGameDay = getOwner().getWorld().getTotalGameTimeDays();
|
||||
this.state = QuestState.QUEST_STATE_UNFINISHED;
|
||||
val triggerCond =
|
||||
questData.getFinishCond().stream()
|
||||
.filter(p -> p.getType() == QuestContent.QUEST_CONTENT_TRIGGER_FIRE)
|
||||
.toList();
|
||||
if (triggerCond.size() > 0) {
|
||||
for (val cond : triggerCond) {
|
||||
TriggerExcelConfigData newTrigger =
|
||||
GameData.getTriggerExcelConfigDataMap().get(cond.getParam()[0]);
|
||||
if (newTrigger != null) {
|
||||
if (this.triggerData == null) {
|
||||
this.triggerData = new HashMap<>();
|
||||
}
|
||||
triggerData.put(newTrigger.getTriggerName(), newTrigger);
|
||||
triggers.put(newTrigger.getTriggerName(), false);
|
||||
SceneGroup group = SceneGroup.of(newTrigger.getGroupId()).load(newTrigger.getSceneId());
|
||||
getOwner()
|
||||
.getWorld()
|
||||
.getSceneById(newTrigger.getSceneId())
|
||||
.loadTriggerFromGroup(group, newTrigger.getTriggerName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
if (ChapterData.beginQuestChapterMap.containsKey(subQuestId)) {
|
||||
getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.beginQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_BEGIN));
|
||||
}
|
||||
|
||||
// Some subQuests and talks become active when some other subQuests are unfinished (even from
|
||||
// different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
getSubQuestId(),
|
||||
getState().getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.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);
|
||||
this.save();
|
||||
}
|
||||
|
||||
public String getTriggerNameById(int id) {
|
||||
TriggerExcelConfigData trigger = GameData.getTriggerExcelConfigDataMap().get(id);
|
||||
if (trigger != null) {
|
||||
String triggerName = trigger.getTriggerName();
|
||||
return triggerName;
|
||||
}
|
||||
// return empty string if can't find trigger
|
||||
return "";
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return this.getMainQuest().getOwner();
|
||||
}
|
||||
|
||||
public void setConfig(QuestData config) {
|
||||
if (config == null || getSubQuestId() != config.getId()) return;
|
||||
this.questData = config;
|
||||
}
|
||||
|
||||
public void setFinishProgress(int index, int value) {
|
||||
finishProgressList[index] = value;
|
||||
}
|
||||
|
||||
public void setFailProgress(int index, int value) {
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
getQuestData()
|
||||
.getFinishExec()
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
// Some subQuests have conditions that subQuests are finished (even from different MainQuests)
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
getOwner()
|
||||
.getQuestManager()
|
||||
.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
|
||||
.getOwner()
|
||||
.sendPacket(
|
||||
new PacketChapterStateNotify(
|
||||
ChapterData.endQuestChapterMap.get(subQuestId).getId(),
|
||||
ChapterStateOuterClass.ChapterState.CHAPTER_STATE_END));
|
||||
}
|
||||
|
||||
// hard coding to give amber
|
||||
if (getQuestData().getSubId() == 35402) {
|
||||
getOwner().getInventory().addItem(1021, 1, ActionReason.QuestItem); // amber item id
|
||||
}
|
||||
|
||||
this.save();
|
||||
|
||||
Grasscutter.getLogger().debug("Quest {} is finished", subQuestId);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void fail() {
|
||||
this.state = QuestState.QUEST_STATE_FAILED;
|
||||
this.finishTime = Utils.getCurrentSeconds();
|
||||
|
||||
this.getOwner().sendPacket(new PacketQuestListUpdateNotify(this));
|
||||
|
||||
// Some subQuests have conditions that subQuests fail (even from different MainQuests)
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_QUEST_STATE_EQUAL,
|
||||
this.subQuestId,
|
||||
this.state.getValue(),
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
this.getOwner()
|
||||
.getQuestManager()
|
||||
.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 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() {
|
||||
getMainQuest().save();
|
||||
}
|
||||
|
||||
public Quest toProto() {
|
||||
Quest.Builder proto =
|
||||
Quest.newBuilder()
|
||||
.setQuestId(getSubQuestId())
|
||||
.setState(getState().getValue())
|
||||
.setParentQuestId(getMainQuestId())
|
||||
.setStartTime(getStartTime())
|
||||
.setStartGameTime(438)
|
||||
.setAcceptTime(getAcceptTime());
|
||||
|
||||
if (getFinishProgressList() != null) {
|
||||
for (int i : getFinishProgressList()) {
|
||||
proto.addFinishProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (getFailProgressList() != null) {
|
||||
for (int i : getFailProgressList()) {
|
||||
proto.addFailProgressList(i);
|
||||
}
|
||||
}
|
||||
|
||||
return proto.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,7 @@ import emu.grasscutter.data.excels.QuestData;
|
||||
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.*;
|
||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
@@ -19,6 +16,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
@@ -86,13 +84,17 @@ public class QuestManager extends BasePlayerManager {
|
||||
this.mainQuests = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
// TODO store user value set on enable
|
||||
public boolean isQuestingEnabled() {
|
||||
return Grasscutter.getConfig().server.game.gameOptions.questing;
|
||||
}
|
||||
|
||||
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);
|
||||
for (GameMainQuest mainQuest : newQuests) {
|
||||
startMainQuest(mainQuest.getParentQuestId());
|
||||
if(this.isQuestingEnabled()) {
|
||||
this.enableQuests();
|
||||
}
|
||||
|
||||
//getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
||||
@@ -117,6 +119,8 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
quest.checkProgress();
|
||||
}
|
||||
|
||||
player.getActivityManager().triggerActivityConditions();
|
||||
}
|
||||
|
||||
public void onTick(){
|
||||
@@ -163,7 +167,8 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
public void enableQuests() {
|
||||
onPlayerBorn();
|
||||
this.triggerEvent(QuestCond.QUEST_COND_NONE, null, 0);
|
||||
this.triggerEvent(QuestCond.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER, null, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -256,6 +261,11 @@ public class QuestManager extends BasePlayerManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
return addQuest(questConfig);
|
||||
}
|
||||
|
||||
public GameQuest addQuest(@Nonnull QuestData questConfig) {
|
||||
|
||||
// Main quest
|
||||
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId());
|
||||
|
||||
@@ -265,7 +275,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
}
|
||||
|
||||
// Sub quest
|
||||
GameQuest quest = mainQuest.getChildQuestById(questId);
|
||||
GameQuest quest = mainQuest.getChildQuestById(questConfig.getSubId());
|
||||
|
||||
// Forcefully start
|
||||
quest.start();
|
||||
@@ -286,13 +296,12 @@ public class QuestManager extends BasePlayerManager {
|
||||
.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);
|
||||
|
||||
}
|
||||
// if (mainQuestId == 355){
|
||||
// startMainQuest(361);
|
||||
// startMainQuest(418);
|
||||
// startMainQuest(423);
|
||||
// startMainQuest(20509);
|
||||
// }
|
||||
}
|
||||
public void queueEvent(QuestCond condType, int... params) {
|
||||
queueEvent(condType, "", params);
|
||||
@@ -312,13 +321,42 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
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();
|
||||
for (GameMainQuest mainquest : checkMainQuests) {
|
||||
mainquest.tryAcceptSubQuests(condType, paramStr, params);
|
||||
var potentialQuests = GameData.getQuestDataByConditions(condType, params[0], paramStr);
|
||||
if(potentialQuests == null){
|
||||
return;
|
||||
}
|
||||
|
||||
var questSystem = getPlayer().getServer().getQuestSystem();
|
||||
var owner = getPlayer();
|
||||
|
||||
potentialQuests.forEach(questData -> {
|
||||
if(this.wasSubQuestStarted(questData)){
|
||||
return;
|
||||
}
|
||||
val acceptCond = questData.getAcceptCond();
|
||||
int[] accept = new int[acceptCond.size()];
|
||||
for (int i = 0; i < acceptCond.size(); i++) {
|
||||
val condition = acceptCond.get(i);
|
||||
boolean result = questSystem.triggerCondition(owner, questData, condition, paramStr, params);
|
||||
accept[i] = result ? 1 : 0;
|
||||
}
|
||||
|
||||
boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept);
|
||||
|
||||
if (shouldAccept){
|
||||
GameQuest quest = owner.getQuestManager().addQuest(questData);
|
||||
Grasscutter.getLogger().debug("Added quest {} result {}", questData.getSubId(), quest !=null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean wasSubQuestStarted(QuestData questData) {
|
||||
var quest = getQuestById(questData.getId());
|
||||
if(quest == null) return false;
|
||||
|
||||
return quest.state != QuestState.QUEST_STATE_UNSTARTED;
|
||||
}
|
||||
|
||||
public void triggerEvent(QuestContent condType, String paramStr, int... params) {
|
||||
Grasscutter.getLogger().debug("Trigger Event {}, {}, {}", condType, paramStr, params);
|
||||
List<GameMainQuest> checkMainQuests = this.getMainQuests().values().stream()
|
||||
@@ -332,6 +370,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
|
||||
/**
|
||||
* TODO maybe trigger them delayed to allow basic communication finish first
|
||||
* TODO move content checks to use static informations where possible to allow direct already fulfilled checking
|
||||
* @param quest
|
||||
*/
|
||||
public void checkQuestAlreadyFullfilled(GameQuest quest){
|
||||
@@ -355,6 +394,7 @@ public class QuestManager extends BasePlayerManager {
|
||||
queueEvent(condition.getType(), condition.getParam()[0], condition.getParam()[1]);
|
||||
}
|
||||
}
|
||||
case QUEST_CONTENT_PLAYER_LEVEL_UP -> queueEvent(condition.getType(), player.getLevel());
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package emu.grasscutter.game.quest.content;
|
||||
|
||||
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
||||
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueContent;
|
||||
|
||||
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
||||
public class ContentUnlockTransPoint extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(
|
||||
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
return condition.getParam()[0] == params[0] && condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest.content;
|
||||
|
||||
import static emu.grasscutter.game.quest.enums.QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT;
|
||||
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueContent;
|
||||
|
||||
@QuestValueContent(QUEST_CONTENT_UNLOCK_TRANS_POINT)
|
||||
public class ContentUnlockTransPoint extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(
|
||||
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
var sceneId = condition.getParam()[0];
|
||||
var scenePointId = condition.getParam()[1];
|
||||
var scenePoints = quest.getOwner().getUnlockedScenePoints().get(sceneId);
|
||||
return scenePoints != null && scenePoints.contains(scenePointId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueExec;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
||||
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
Grasscutter.getLogger().info("Energy refilled");
|
||||
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.QuestData;
|
||||
import emu.grasscutter.game.quest.GameQuest;
|
||||
import emu.grasscutter.game.quest.QuestValueExec;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_ADD_CUR_AVATAR_ENERGY)
|
||||
public class ExecAddCurAvatarEnergy extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
Grasscutter.getLogger().debug("Energy refilled");
|
||||
return quest.getOwner().getEnergyManager().refillActiveEnergy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,12 +319,14 @@ public class World implements Iterable<Player> {
|
||||
var newScene = this.getSceneById(teleportProperties.getSceneId());
|
||||
newScene.addPlayer(player);
|
||||
player.setAvatarsAbilityForScene(newScene);
|
||||
|
||||
// Dungeon
|
||||
// Dungeon system is handling this already
|
||||
// if(dungeonData!=null){
|
||||
// var dungeonManager = new DungeonManager(newScene, dungeonData);
|
||||
// dungeonManager.startDungeon();
|
||||
// }
|
||||
|
||||
SceneConfig config = newScene.getScriptManager().getConfig();
|
||||
if (teleportProperties.getTeleportTo() == null && config != null) {
|
||||
if (config.born_pos != null) {
|
||||
|
||||
@@ -1,153 +1,160 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.InvestigationMonsterData;
|
||||
import emu.grasscutter.data.excels.RewardPreviewData;
|
||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.InvestigationMonsterOuterClass;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.SceneMonster;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class WorldDataSystem extends BaseGameSystem {
|
||||
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
|
||||
|
||||
public WorldDataSystem(GameServer server) {
|
||||
super(server);
|
||||
this.chestInteractHandlerMap = new HashMap<>();
|
||||
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
|
||||
|
||||
loadChestConfig();
|
||||
}
|
||||
|
||||
public synchronized void loadChestConfig() {
|
||||
// set the special chest first
|
||||
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||
|
||||
try {
|
||||
DataLoader.loadList("ChestReward.json", ChestReward.class)
|
||||
.forEach(
|
||||
reward ->
|
||||
reward
|
||||
.getObjNames()
|
||||
.forEach(
|
||||
name ->
|
||||
chestInteractHandlerMap.computeIfAbsent(
|
||||
name, x -> new NormalChestInteractHandler(reward))));
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
||||
return chestInteractHandlerMap;
|
||||
}
|
||||
|
||||
public RewardPreviewData getRewardByBossId(int monsterId) {
|
||||
var investigationMonsterData =
|
||||
GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||
.findFirst();
|
||||
|
||||
if (investigationMonsterData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return GameData.getRewardPreviewDataMap()
|
||||
.get(investigationMonsterData.get().getRewardPreviewId());
|
||||
}
|
||||
|
||||
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
|
||||
var key = sceneId + "_" + groupId;
|
||||
if (!sceneInvestigationGroupMap.containsKey(key)) {
|
||||
var group = SceneGroup.of(groupId).load(sceneId);
|
||||
sceneInvestigationGroupMap.putIfAbsent(key, group);
|
||||
return group;
|
||||
}
|
||||
return sceneInvestigationGroupMap.get(key);
|
||||
}
|
||||
|
||||
public int getMonsterLevel(SceneMonster monster, World world) {
|
||||
// Calculate level
|
||||
int level = monster.level;
|
||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
|
||||
|
||||
if (worldLevelData != null) {
|
||||
level = worldLevelData.getMonsterLevel();
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
private InvestigationMonsterOuterClass.InvestigationMonster getInvestigationMonster(
|
||||
Player player, InvestigationMonsterData imd) {
|
||||
if (imd.getGroupIdList().isEmpty() || imd.getMonsterIdList().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var groupId = imd.getGroupIdList().get(0);
|
||||
var monsterId = imd.getMonsterIdList().get(0);
|
||||
var sceneId = imd.getCityData().getSceneId();
|
||||
var group = getInvestigationGroup(sceneId, groupId);
|
||||
|
||||
if (group == null || group.monsters == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var monster =
|
||||
group.monsters.values().stream().filter(x -> x.monster_id == monsterId).findFirst();
|
||||
if (monster.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = InvestigationMonsterOuterClass.InvestigationMonster.newBuilder();
|
||||
|
||||
builder
|
||||
.setId(imd.getId())
|
||||
.setCityId(imd.getCityId())
|
||||
.setSceneId(imd.getCityData().getSceneId())
|
||||
.setGroupId(groupId)
|
||||
.setMonsterId(monsterId)
|
||||
.setLevel(getMonsterLevel(monster.get(), player.getWorld()))
|
||||
.setIsAlive(true)
|
||||
.setNextRefreshTime(Integer.MAX_VALUE)
|
||||
.setRefreshInterval(Integer.MAX_VALUE)
|
||||
.setPos(monster.get().pos.toProto());
|
||||
|
||||
if ("Boss".equals(imd.getMonsterCategory())) {
|
||||
var bossChest = group.searchBossChestInGroup();
|
||||
if (bossChest.isPresent()) {
|
||||
builder.setResin(bossChest.get().resin);
|
||||
builder.setMaxBossChestNum(bossChest.get().take_num);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(
|
||||
Player player, int cityId) {
|
||||
var cityData = GameData.getCityDataMap().get(cityId);
|
||||
if (cityData == null) {
|
||||
Grasscutter.getLogger().warn("City not exist {}", cityId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getCityId() == cityId)
|
||||
.map(imd -> this.getInvestigationMonster(player, imd))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.InvestigationMonsterData;
|
||||
import emu.grasscutter.data.excels.RewardPreviewData;
|
||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.InvestigationMonsterOuterClass;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.SceneMonster;
|
||||
import emu.grasscutter.server.game.BaseGameSystem;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import org.luaj.vm2.LuaError;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class WorldDataSystem extends BaseGameSystem {
|
||||
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||
private final Map<String, SceneGroup> sceneInvestigationGroupMap; // <sceneId_groupId, Group>
|
||||
|
||||
public WorldDataSystem(GameServer server) {
|
||||
super(server);
|
||||
this.chestInteractHandlerMap = new HashMap<>();
|
||||
this.sceneInvestigationGroupMap = new ConcurrentHashMap<>();
|
||||
|
||||
loadChestConfig();
|
||||
}
|
||||
|
||||
public synchronized void loadChestConfig() {
|
||||
// set the special chest first
|
||||
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||
|
||||
try {
|
||||
DataLoader.loadList("ChestReward.json", ChestReward.class)
|
||||
.forEach(
|
||||
reward ->
|
||||
reward
|
||||
.getObjNames()
|
||||
.forEach(
|
||||
name ->
|
||||
chestInteractHandlerMap.computeIfAbsent(
|
||||
name, x -> new NormalChestInteractHandler(reward))));
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
||||
return chestInteractHandlerMap;
|
||||
}
|
||||
|
||||
public RewardPreviewData getRewardByBossId(int monsterId) {
|
||||
var investigationMonsterData =
|
||||
GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||
.findFirst();
|
||||
|
||||
if (investigationMonsterData.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return GameData.getRewardPreviewDataMap()
|
||||
.get(investigationMonsterData.get().getRewardPreviewId());
|
||||
}
|
||||
|
||||
private SceneGroup getInvestigationGroup(int sceneId, int groupId) {
|
||||
var key = sceneId + "_" + groupId;
|
||||
if (!sceneInvestigationGroupMap.containsKey(key)) {
|
||||
try {
|
||||
var group = SceneGroup.of(groupId).load(sceneId);
|
||||
sceneInvestigationGroupMap.putIfAbsent(key, group);
|
||||
return group;
|
||||
} catch (LuaError luaError) {
|
||||
Grasscutter.getLogger()
|
||||
.error("failed to get investigationGroup {} in scene{}:", groupId, sceneId, luaError);
|
||||
}
|
||||
}
|
||||
return sceneInvestigationGroupMap.get(key);
|
||||
}
|
||||
|
||||
public int getMonsterLevel(SceneMonster monster, World world) {
|
||||
// Calculate level
|
||||
int level = monster.level;
|
||||
WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel());
|
||||
|
||||
if (worldLevelData != null) {
|
||||
level = worldLevelData.getMonsterLevel();
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
private InvestigationMonsterOuterClass.InvestigationMonster getInvestigationMonster(
|
||||
Player player, InvestigationMonsterData imd) {
|
||||
if (imd.getGroupIdList().isEmpty() || imd.getMonsterIdList().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var groupId = imd.getGroupIdList().get(0);
|
||||
var monsterId = imd.getMonsterIdList().get(0);
|
||||
var sceneId = imd.getCityData().getSceneId();
|
||||
var group = getInvestigationGroup(sceneId, groupId);
|
||||
|
||||
if (group == null || group.monsters == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var monster =
|
||||
group.monsters.values().stream().filter(x -> x.monster_id == monsterId).findFirst();
|
||||
if (monster.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var builder = InvestigationMonsterOuterClass.InvestigationMonster.newBuilder();
|
||||
|
||||
builder
|
||||
.setId(imd.getId())
|
||||
.setCityId(imd.getCityId())
|
||||
.setSceneId(imd.getCityData().getSceneId())
|
||||
.setGroupId(groupId)
|
||||
.setMonsterId(monsterId)
|
||||
.setLevel(getMonsterLevel(monster.get(), player.getWorld()))
|
||||
.setIsAlive(true)
|
||||
.setNextRefreshTime(Integer.MAX_VALUE)
|
||||
.setRefreshInterval(Integer.MAX_VALUE)
|
||||
.setPos(monster.get().pos.toProto());
|
||||
|
||||
if ("Boss".equals(imd.getMonsterCategory())) {
|
||||
var bossChest = group.searchBossChestInGroup();
|
||||
if (bossChest.isPresent()) {
|
||||
builder.setResin(bossChest.get().resin);
|
||||
builder.setMaxBossChestNum(bossChest.get().take_num);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public List<InvestigationMonsterOuterClass.InvestigationMonster> getInvestigationMonstersByCityId(
|
||||
Player player, int cityId) {
|
||||
var cityData = GameData.getCityDataMap().get(cityId);
|
||||
if (cityData == null) {
|
||||
Grasscutter.getLogger().warn("City not exist {}", cityId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getCityId() == cityId)
|
||||
.map(imd -> this.getInvestigationMonster(player, imd))
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user