Implement quests

This commit is contained in:
Melledy 2022-05-11 03:56:40 -07:00
parent ef9d63f1dd
commit 285405cee4
47 changed files with 1379 additions and 6 deletions

9
proto/ChildQuest.proto Normal file
View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ChildQuest {
uint32 quest_id = 1;
uint32 state = 2;
uint32 quest_config_id = 3;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneBeginNotify {
uint32 cutscene_id = 1;
bool is_wait_others = 2;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneEndNotify {
int32 retcode = 1;
uint32 cutscene_id = 2;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message CutSceneFinishNotify {
uint32 cutscene_id = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuest.proto";
message FinishedParentQuestNotify {
repeated ParentQuest parent_quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuest.proto";
message FinishedParentQuestUpdateNotify {
repeated ParentQuest parent_quest_list = 1;
}

18
proto/ParentQuest.proto Normal file
View File

@ -0,0 +1,18 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "ParentQuestRandomInfo.proto";
import "ChildQuest.proto";
message ParentQuest {
uint32 parent_quest_id = 1;
repeated ChildQuest child_quest_list = 2;
bool is_finished = 3;
bool is_random = 4;
ParentQuestRandomInfo random_info = 5;
repeated int32 quest_var = 6;
uint32 parent_quest_state = 7;
uint32 quest_var_seq = 8;
map<uint32, uint32> time_var_map = 9;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ParentQuestRandomInfo {
uint32 entrance_id = 1;
uint32 template_id = 2;
repeated uint32 factor_list = 3;
}

20
proto/Quest.proto Normal file
View File

@ -0,0 +1,20 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message Quest {
uint32 quest_id = 1;
uint32 state = 2;
uint32 start_time = 4;
bool is_random = 5;
uint32 parent_quest_id = 6;
uint32 quest_config_id = 7;
uint32 start_game_time = 8;
uint32 accept_time = 9;
repeated uint32 lacked_npc_list = 10;
repeated uint32 finish_progress_list = 11;
repeated uint32 fail_progress_list = 12;
map<uint32, uint32> lacked_npc_map = 13;
repeated uint32 lacked_place_list = 14;
map<uint32, uint32> lacked_place_map = 15;
}

View File

@ -0,0 +1,7 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestDelNotify {
uint32 quest_id = 1;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestGlobalVar {
uint32 key = 1;
int32 value = 2;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "QuestGlobalVar.proto";
message QuestGlobalVarNotify {
repeated QuestGlobalVar var_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Quest.proto";
message QuestListNotify {
repeated Quest quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "Quest.proto";
message QuestListUpdateNotify {
repeated Quest quest_list = 1;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestProgressUpdateNotify {
uint32 quest_id = 1;
repeated uint32 finish_progress_list = 2;
repeated uint32 fail_progress_list = 3;
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestUpdateQuestVarNotify {
uint32 parent_quest_id = 1;
repeated int32 quest_var = 2;
uint32 parent_quest_var_seq = 3;
}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
import "QuestVarOp.proto";
message QuestUpdateQuestVarReq {
uint32 quest_id = 1;
repeated QuestVarOp quest_var_op_list = 2;
uint32 parent_quest_id = 3;
uint32 parent_quest_var_seq = 4;
}

View File

@ -0,0 +1,10 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestUpdateQuestVarRsp {
int32 retcode = 1;
uint32 quest_id = 2;
uint32 parent_quest_id = 3;
uint32 parent_quest_var_seq = 4;
}

9
proto/QuestVarOp.proto Normal file
View File

@ -0,0 +1,9 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message QuestVarOp {
uint32 index = 1;
int32 value = 2;
bool is_add = 3;
}

View File

@ -0,0 +1,8 @@
syntax = "proto3";
option java_package = "emu.grasscutter.net.proto";
message ServerCondMeetQuestListUpdateNotify {
repeated uint32 add_quest_id_list = 1;
repeated uint32 del_quest_id_list = 2;
}

View File

@ -0,0 +1,66 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameQuest;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "quest", usage = "quest <add|finish> [quest id]", permission = "player.quest", permissionTargeted = "player.quest.others", description = "commands.quest.description")
public final class QuestCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (targetPlayer == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.need_target"));
return;
}
if (args.size() != 2) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
return;
}
String cmd = args.get(0).toLowerCase();
int questId;
try {
questId = Integer.parseInt(args.get(1));
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.invalid_id"));
return;
}
switch (cmd) {
case "add" -> {
GameQuest quest = sender.getQuestManager().addQuest(questId);
if (quest != null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.added", questId));
return;
}
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found"));
}
case "finish" -> {
GameQuest quest = sender.getQuestManager().getQuestById(questId);
if (quest == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found"));
return;
}
quest.finish();
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.finished", questId));
}
default -> {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
}
}
}
}

View File

@ -12,6 +12,7 @@ import emu.grasscutter.data.custom.AbilityEmbryoEntry;
import emu.grasscutter.data.custom.AbilityModifier;
import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.data.def.*;
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
@ -27,6 +28,7 @@ public class GameData {
private static final Map<String, AbilityModifierEntry> abilityModifiers = new HashMap<>();
private static final Map<String, OpenConfigEntry> openConfigEntries = new HashMap<>();
private static final Map<String, ScenePointEntry> scenePointEntries = new HashMap<>();
private static final Int2ObjectMap<QuestConfig> questConfigs = new Int2ObjectOpenHashMap<>();
// ExcelConfigs
private static final Int2ObjectMap<PlayerLevelData> playerLevelDataMap = new Int2ObjectOpenHashMap<>();
@ -122,6 +124,10 @@ public class GameData {
return getScenePointEntries().get(sceneId + "_" + pointId);
}
public static Int2ObjectMap<QuestConfig> getQuestConfigs() {
return questConfigs;
}
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
return avatarDataMap;
}

View File

@ -24,6 +24,9 @@ import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType;
import emu.grasscutter.data.custom.AbilityModifierEntry;
import emu.grasscutter.data.custom.OpenConfigEntry;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.data.custom.QuestConfigData;
import emu.grasscutter.data.custom.QuestConfigData.SubQuestConfigData;
import emu.grasscutter.data.custom.ScenePointEntry;
import emu.grasscutter.game.world.SpawnDataEntry;
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
@ -57,8 +60,9 @@ public class ResourceLoader {
loadResources();
// Process into depots
GameDepot.load();
// Load spawn data
// Load spawn data and quests
loadSpawnData();
loadQuests();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints();
// Custom - TODO move this somewhere else
@ -396,6 +400,34 @@ public class ResourceLoader {
GameData.getOpenConfigEntries().put(entry.getName(), entry);
}
}
private static void loadQuests() {
File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Quest/");
if (!folder.exists()) {
return;
}
for (File file : folder.listFiles()) {
QuestConfigData mainQuest = null;
try (FileReader fileReader = new FileReader(file)) {
mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, QuestConfigData.class);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (mainQuest.getSubQuests() != null) {
for (SubQuestConfigData subQuest : mainQuest.getSubQuests()) {
QuestConfig quest = new QuestConfig(mainQuest, subQuest);
GameData.getQuestConfigs().put(quest.getId(), quest);
}
}
}
Grasscutter.getLogger().info("Loaded " + GameData.getQuestConfigs().size() + " Quest Configs");
}
// BinOutput configs

View File

@ -0,0 +1,25 @@
package emu.grasscutter.data.custom;
import emu.grasscutter.data.custom.QuestConfigData.SubQuestConfigData;
public class QuestConfig {
private final QuestConfigData mainQuest;
private final SubQuestConfigData subQuest;
public QuestConfig(QuestConfigData mainQuest, SubQuestConfigData subQuest) {
this.mainQuest = mainQuest;
this.subQuest = subQuest;
}
public int getId() {
return subQuest.getSubId();
}
public QuestConfigData getMainQuest() {
return mainQuest;
}
public SubQuestConfigData getSubQuest() {
return subQuest;
}
}

View File

@ -0,0 +1,104 @@
package emu.grasscutter.data.custom;
import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestCondType;
import emu.grasscutter.game.quest.enums.QuestType;
public class QuestConfigData {
private int id;
private int series;
private QuestType type;
private long titleTextMapHash;
private int[] suggestTrackMainQuestList;
private int[] rewardIdList;
private SubQuestConfigData[] subQuests;
public int getId() {
return id;
}
public int getSeries() {
return series;
}
public QuestType getType() {
return type;
}
public long getTitleTextMapHash() {
return titleTextMapHash;
}
public int[] getSuggestTrackMainQuestList() {
return suggestTrackMainQuestList;
}
public int[] getRewardIdList() {
return rewardIdList;
}
public SubQuestConfigData[] getSubQuests() {
return subQuests;
}
public class SubQuestConfigData {
private int subId;
private int mainId;
private LogicType acceptCondComb;
private QuestCondition[] acceptCond;
private LogicType finishCondComb;
private QuestCondition[] finishCond;
private LogicType failCondComb;
private QuestCondition[] failCond;
public int getSubId() {
return subId;
}
public int getMainId() {
return mainId;
}
public LogicType getAcceptCondComb() {
return acceptCondComb;
}
public QuestCondition[] getAcceptCond() {
return acceptCond;
}
public LogicType getFinishCondComb() {
return finishCondComb;
}
public QuestCondition[] getFinishCond() {
return finishCond;
}
public LogicType getFailCondComb() {
return failCondComb;
}
public QuestCondition[] getFailCond() {
return failCond;
}
}
public class QuestCondition {
private QuestCondType type;
private int[] param;
public QuestCondType getType() {
return type;
}
public int[] getParam() {
return param;
}
}
}

View File

@ -15,6 +15,7 @@ import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import static com.mongodb.client.model.Filters.eq;
@ -111,6 +112,8 @@ public final class DatabaseHelper {
DatabaseManager.getDatabase().getCollection("gachas").deleteMany(eq("ownerId", target.getPlayerUid()));
// Delete GameItem.class data
DatabaseManager.getDatabase().getCollection("items").deleteMany(eq("ownerId", target.getPlayerUid()));
// Delete GameMainQuest.class data
DatabaseManager.getDatabase().getCollection("quests").deleteMany(eq("ownerUid", target.getPlayerUid()));
// Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships,
@ -260,4 +263,16 @@ public final class DatabaseHelper {
DeleteResult result = DatabaseManager.getDatastore().delete(mail);
return result.wasAcknowledged();
}
public static List<GameMainQuest> getAllQuests(Player player) {
return DatabaseManager.getDatastore().find(GameMainQuest.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList();
}
public static void saveQuest(GameMainQuest quest) {
DatabaseManager.getDatastore().save(quest);
}
public static boolean deleteQuest(GameMainQuest quest) {
return DatabaseManager.getDatastore().delete(quest).wasAcknowledged();
}
}

View File

@ -20,6 +20,8 @@ import emu.grasscutter.game.gacha.GachaRecord;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.mail.Mail;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
public final class DatabaseManager {
@ -30,7 +32,8 @@ public final class DatabaseManager {
private static Datastore dispatchDatastore;
private static final Class<?>[] mappedClasses = new Class<?>[] {
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class, Mail.class
DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class,
GachaRecord.class, Mail.class, GameMainQuest.class
};
public static Datastore getDatastore() {

View File

@ -29,6 +29,9 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.game.shop.ShopLimit;
import emu.grasscutter.game.managers.MapMarkManager.*;
import emu.grasscutter.game.tower.TowerManager;
@ -91,6 +94,7 @@ public class Player {
@Transient private MailHandler mailHandler;
@Transient private MessageHandler messageHandler;
@Transient private AbilityManager abilityManager;
@Transient private QuestManager questManager;
@Transient private SotSManager sotsManager;
@ -145,6 +149,7 @@ public class Player {
this.mailHandler = new MailHandler(this);
this.towerManager = new TowerManager(this);
this.abilityManager = new AbilityManager(this);
this.setQuestManager(new QuestManager(this));
this.pos = new Position();
this.rotation = new Position();
this.properties = new HashMap<>();
@ -409,6 +414,14 @@ public class Player {
return towerManager;
}
public QuestManager getQuestManager() {
return questManager;
}
public void setQuestManager(QuestManager questManager) {
this.questManager = questManager;
}
public PlayerGachaInfo getGachaInfo() {
return gachaInfo;
}
@ -883,9 +896,7 @@ public class Player {
}
public void sendPacket(BasePacket packet) {
if (this.hasSentAvatarDataNotify) {
this.getSession().send(packet);
}
this.getSession().send(packet);
}
public OnlinePlayerInfo getOnlinePlayerInfo() {
@ -1118,7 +1129,23 @@ public class Player {
this.getFriendsList().loadFromDatabase();
this.getMailHandler().loadFromDatabase();
this.getQuestManager().loadFromDatabase();
// Quest - Commented out because a problem is caused if you log out while this quest is active
/*
if (getQuestManager().getMainQuestById(351) == null) {
GameQuest quest = getQuestManager().addQuest(35104);
if (quest != null) {
quest.finish();
}
getQuestManager().addQuest(35101);
this.setSceneId(3);
this.getPos().set(GameConstants.START_POSITION);
}
*/
// Create world
World world = new World(this);
world.addPlayer(this);
@ -1138,7 +1165,10 @@ public class Player {
session.send(new PacketStoreWeightLimitNotify());
session.send(new PacketPlayerStoreNotify(this));
session.send(new PacketAvatarDataNotify(this));
session.send(new PacketFinishedParentQuestNotify(this));
session.send(new PacketQuestListNotify(this));
session.send(new PacketServerCondMeetQuestListUpdateNotify(this));
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world

View File

@ -0,0 +1,124 @@
package emu.grasscutter.game.quest;
import java.util.HashMap;
import java.util.Map;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.ParentQuestState;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest;
import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest;
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@Entity(value = "quests", useDiscriminator = false)
public class GameMainQuest {
@Id private ObjectId id;
@Indexed private int ownerUid;
@Transient private Player owner;
private Map<Integer, GameQuest> childQuests;
private int parentQuestId;
private int[] questVars;
private ParentQuestState state;
private boolean isFinished;
@Deprecated // Morphia only. Do not use.
public GameMainQuest() {}
public GameMainQuest(Player player, int parentQuestId) {
this.owner = player;
this.ownerUid = player.getUid();
this.parentQuestId = parentQuestId;
this.childQuests = new HashMap<>();
this.questVars = new int[5];
this.state = ParentQuestState.PARENT_QUEST_STATE_NONE;
}
public int getParentQuestId() {
return parentQuestId;
}
public int getOwnerUid() {
return ownerUid;
}
public Player getOwner() {
return owner;
}
public void setOwner(Player player) {
if (player.getUid() != this.getOwnerUid()) return;
this.owner = player;
}
public Map<Integer, GameQuest> getChildQuests() {
return childQuests;
}
public GameQuest getChildQuestById(int id) {
return this.getChildQuests().get(id);
}
public int[] getQuestVars() {
return questVars;
}
public ParentQuestState getState() {
return state;
}
public boolean isFinished() {
return isFinished;
}
public void finish() {
this.isFinished = true;
this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED;
this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this));
}
public void save() {
DatabaseHelper.saveQuest(this);
}
public ParentQuest toProto() {
ParentQuest.Builder proto = ParentQuest.newBuilder()
.setParentQuestId(getParentQuestId())
.setIsFinished(isFinished())
.setParentQuestState(getState().getValue());
for (GameQuest quest : this.getChildQuests().values()) {
ChildQuest childQuest = ChildQuest.newBuilder()
.setQuestId(quest.getQuestId())
.setState(quest.getState().getValue())
.build();
proto.addChildQuestList(childQuest);
}
if (getQuestVars() != null) {
for (int i : getQuestVars()) {
proto.addQuestVar(i);
}
}
return proto.build();
}
}

View File

@ -0,0 +1,188 @@
package emu.grasscutter.game.quest;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.data.custom.QuestConfigData.SubQuestConfigData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.net.proto.QuestOuterClass.Quest;
import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify;
import emu.grasscutter.utils.Utils;
@Entity
public class GameQuest {
@Transient private GameMainQuest mainQuest;
@Transient private QuestConfig config;
private int questId;
private int mainQuestId;
private QuestState state;
private int startTime;
private int acceptTime;
private int finishTime;
private int[] finishProgressList;
private int[] failProgressList;
@Deprecated // Morphia only. Do not use.
public GameQuest() {}
public GameQuest(GameMainQuest mainQuest, QuestConfig config) {
this.mainQuest = mainQuest;
this.questId = config.getId();
this.mainQuestId = config.getMainQuest().getId();
this.config = config;
this.acceptTime = Utils.getCurrentSeconds();
this.startTime = this.acceptTime;
this.state = QuestState.QUEST_STATE_UNFINISHED;
if (config.getSubQuest().getFinishCond() != null) {
this.finishProgressList = new int[config.getSubQuest().getFinishCond().length];
}
if (config.getSubQuest().getFailCond() != null) {
this.failProgressList = new int[config.getSubQuest().getFailCond().length];
}
this.mainQuest.getChildQuests().put(this.questId, this);
}
public GameMainQuest getMainQuest() {
return mainQuest;
}
public void setMainQuest(GameMainQuest mainQuest) {
this.mainQuest = mainQuest;
}
public Player getOwner() {
return getMainQuest().getOwner();
}
public int getQuestId() {
return questId;
}
public int getMainQuestId() {
return mainQuestId;
}
public QuestConfig getConfig() {
return config;
}
public void setConfig(QuestConfig config) {
if (this.getQuestId() != config.getId()) return;
this.config = config;
}
public QuestState getState() {
return state;
}
public void setState(QuestState state) {
this.state = state;
}
public int getStartTime() {
return startTime;
}
public void setStartTime(int startTime) {
this.startTime = startTime;
}
public int getAcceptTime() {
return acceptTime;
}
public void setAcceptTime(int acceptTime) {
this.acceptTime = acceptTime;
}
public int getFinishTime() {
return finishTime;
}
public void setFinishTime(int finishTime) {
this.finishTime = finishTime;
}
public int[] getFinishProgressList() {
return finishProgressList;
}
public void setFinishProgress(int index, int value) {
finishProgressList[index] = value;
}
public int[] getFailProgressList() {
return failProgressList;
}
public void setFailProgress(int index, int value) {
failProgressList[index] = value;
}
public void finish() {
this.state = QuestState.QUEST_STATE_FINISHED;
this.finishTime = Utils.getCurrentSeconds();
if (this.getFinishProgressList() != null) {
for (int i = 0 ; i < getFinishProgressList().length; i++) {
getFinishProgressList()[i] = 1;
}
}
this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this));
// Finish main quest if all child quests are done
this.tryFinishMainQuest();
this.save();
}
public boolean tryFinishMainQuest() {
try {
SubQuestConfigData subQuestData = getConfig().getMainQuest().getSubQuests()[getConfig().getMainQuest().getSubQuests().length - 1];
if (subQuestData.getSubId() == this.getQuestId()) {
getMainQuest().finish();
return true;
}
} catch (Exception e) {
}
return false;
}
public void save() {
getMainQuest().save();
}
public Quest toProto() {
Quest.Builder proto = Quest.newBuilder()
.setQuestId(this.getQuestId())
.setState(this.getState().getValue())
.setParentQuestId(this.getMainQuestId())
.setStartTime(this.getStartTime())
.setStartGameTime(438)
.setAcceptTime(this.getAcceptTime());
if (this.getFinishProgressList() != null) {
for (int i : this.getFinishProgressList()) {
proto.addFinishProgressList(i);
}
}
if (this.getFailProgressList() != null) {
for (int i : this.getFailProgressList()) {
proto.addFailProgressList(i);
}
}
return proto.build();
}
}

View File

@ -0,0 +1,119 @@
package emu.grasscutter.game.quest;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify;
import emu.grasscutter.server.packet.send.PacketServerCondMeetQuestListUpdateNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public class QuestManager {
private final Player player;
private final Int2ObjectMap<GameMainQuest> quests;
public QuestManager(Player player) {
this.player = player;
this.quests = new Int2ObjectOpenHashMap<>();
}
public Player getPlayer() {
return player;
}
public Int2ObjectMap<GameMainQuest> getQuests() {
return quests;
}
public GameMainQuest getMainQuestById(int mainQuestId) {
return getQuests().get(mainQuestId);
}
public GameQuest getQuestById(int questId) {
QuestConfig questConfig = GameData.getQuestConfigs().get(questId);
if (questConfig == null) {
return null;
}
GameMainQuest mainQuest = getQuests().get(questConfig.getMainQuest().getId());
if (mainQuest == null) {
return null;
}
return mainQuest.getChildQuests().get(questId);
}
public void forEachQuest(Consumer<GameQuest> callback) {
for (GameMainQuest mainQuest : getQuests().values()) {
for (GameQuest quest : mainQuest.getChildQuests().values()) {
callback.accept(quest);
}
}
}
public GameMainQuest addMainQuest(QuestConfig questConfig) {
GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainQuest().getId());
getQuests().put(mainQuest.getParentQuestId(), mainQuest);
getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest));
return mainQuest;
}
public GameQuest addQuest(int questId) {
QuestConfig questConfig = GameData.getQuestConfigs().get(questId);
if (questConfig == null) {
return null;
}
// Main quest
GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainQuest().getId());
// Create main quest if it doesnt exist
if (mainQuest == null) {
mainQuest = addMainQuest(questConfig);
}
// Sub quest
GameQuest quest = mainQuest.getChildQuestById(questId);
if (quest != null) {
return null;
}
// Create
quest = new GameQuest(mainQuest, questConfig);
// Save main quest
mainQuest.save();
// Send packet
getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(quest));
getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest));
return quest;
}
public void loadFromDatabase() {
List<GameMainQuest> quests = DatabaseHelper.getAllQuests(getPlayer());
for (GameMainQuest mainQuest : quests) {
mainQuest.setOwner(this.getPlayer());
for (GameQuest quest : mainQuest.getChildQuests().values()) {
quest.setMainQuest(mainQuest);
quest.setConfig(GameData.getQuestConfigs().get(quest.getQuestId()));
}
this.getQuests().put(mainQuest.getParentQuestId(), mainQuest);
}
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.game.quest.enums;
public enum LogicType {
LOGIC_NONE (0),
LOGIC_AND (1),
LOGIC_OR (2),
LOGIC_NOT (3),
LOGIC_A_AND_ETCOR (4),
LOGIC_A_AND_B_AND_ETCOR (5),
LOGIC_A_OR_ETCAND (6),
LOGIC_A_OR_B_OR_ETCAND (7),
LOGIC_A_AND_B_OR_ETCAND (8);
private final int value;
LogicType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,18 @@
package emu.grasscutter.game.quest.enums;
public enum ParentQuestState {
PARENT_QUEST_STATE_NONE (0),
PARENT_QUEST_STATE_FINISHED (1),
PARENT_QUEST_STATE_FAILED (2),
PARENT_QUEST_STATE_CANCELED (3);
private final int value;
ParentQuestState(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,92 @@
package emu.grasscutter.game.quest.enums;
public enum QuestCondType {
QUEST_COND_NONE (0),
QUEST_COND_STATE_EQUAL (1),
QUEST_COND_STATE_NOT_EQUAL (2),
QUEST_COND_PACK_HAVE_ITEM (3),
QUEST_COND_AVATAR_ELEMENT_EQUAL (4),
QUEST_COND_AVATAR_ELEMENT_NOT_EQUAL (5),
QUEST_COND_AVATAR_CAN_CHANGE_ELEMENT (6),
QUEST_COND_CITY_LEVEL_EQUAL_GREATER (7),
QUEST_COND_ITEM_NUM_LESS_THAN (8),
QUEST_COND_DAILY_TASK_START (9),
QUEST_COND_OPEN_STATE_EQUAL (10),
QUEST_COND_DAILY_TASK_OPEN (11),
QUEST_COND_DAILY_TASK_REWARD_CAN_GET (12),
QUEST_COND_DAILY_TASK_REWARD_RECEIVED (13),
QUEST_COND_PLAYER_LEVEL_REWARD_CAN_GET (14),
QUEST_COND_EXPLORATION_REWARD_CAN_GET (15),
QUEST_COND_IS_WORLD_OWNER (16),
QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER (17),
QUEST_COND_SCENE_AREA_UNLOCKED (18),
QUEST_COND_ITEM_GIVING_ACTIVED (19),
QUEST_COND_ITEM_GIVING_FINISHED (20),
QUEST_COND_IS_DAYTIME (21),
QUEST_COND_CURRENT_AVATAR (22),
QUEST_COND_CURRENT_AREA (23),
QUEST_COND_QUEST_VAR_EQUAL (24),
QUEST_COND_QUEST_VAR_GREATER (25),
QUEST_COND_QUEST_VAR_LESS (26),
QUEST_COND_FORGE_HAVE_FINISH (27),
QUEST_COND_DAILY_TASK_IN_PROGRESS (28),
QUEST_COND_DAILY_TASK_FINISHED (29),
QUEST_COND_ACTIVITY_COND (30),
QUEST_COND_ACTIVITY_OPEN (31),
QUEST_COND_DAILY_TASK_VAR_GT (32),
QUEST_COND_DAILY_TASK_VAR_EQ (33),
QUEST_COND_DAILY_TASK_VAR_LT (34),
QUEST_COND_BARGAIN_ITEM_GT (35),
QUEST_COND_BARGAIN_ITEM_EQ (36),
QUEST_COND_BARGAIN_ITEM_LT (37),
QUEST_COND_COMPLETE_TALK (38),
QUEST_COND_NOT_HAVE_BLOSSOM_TALK (39),
QUEST_COND_IS_CUR_BLOSSOM_TALK (40),
QUEST_COND_QUEST_NOT_RECEIVE (41),
QUEST_COND_QUEST_SERVER_COND_VALID (42),
QUEST_COND_ACTIVITY_CLIENT_COND (43),
QUEST_COND_QUEST_GLOBAL_VAR_EQUAL (44),
QUEST_COND_QUEST_GLOBAL_VAR_GREATER (45),
QUEST_COND_QUEST_GLOBAL_VAR_LESS (46),
QUEST_COND_PERSONAL_LINE_UNLOCK (47),
QUEST_COND_CITY_REPUTATION_REQUEST (48),
QUEST_COND_MAIN_COOP_START (49),
QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT (50),
QUEST_COND_CITY_REPUTATION_LEVEL (51),
QUEST_COND_CITY_REPUTATION_UNLOCK (52),
QUEST_COND_LUA_NOTIFY (53),
QUEST_COND_CUR_CLIMATE (54),
QUEST_COND_ACTIVITY_END (55),
QUEST_COND_COOP_POINT_RUNNING (56),
QUEST_COND_GADGET_TALK_STATE_EQUAL (57),
QUEST_COND_AVATAR_FETTER_GT (58),
QUEST_COND_AVATAR_FETTER_EQ (59),
QUEST_COND_AVATAR_FETTER_LT (60),
QUEST_COND_NEW_HOMEWORLD_MOUDLE_UNLOCK (61),
QUEST_COND_NEW_HOMEWORLD_LEVEL_REWARD (62),
QUEST_COND_NEW_HOMEWORLD_MAKE_FINISH (63),
QUEST_COND_HOMEWORLD_NPC_EVENT (64),
QUEST_COND_TIME_VAR_GT_EQ (65),
QUEST_COND_TIME_VAR_PASS_DAY (66),
QUEST_COND_HOMEWORLD_NPC_NEW_TALK (67),
QUEST_COND_PLAYER_CHOOSE_MALE (68),
QUEST_COND_HISTORY_GOT_ANY_ITEM (69),
QUEST_COND_LEARNED_RECIPE (70),
QUEST_COND_LUNARITE_REGION_UNLOCKED (71),
QUEST_COND_LUNARITE_HAS_REGION_HINT_COUNT (72),
QUEST_COND_LUNARITE_COLLECT_FINISH (73),
QUEST_COND_LUNARITE_MARK_ALL_FINISH (74),
QUEST_COND_NEW_HOMEWORLD_SHOP_ITEM (75),
QUEST_COND_SCENE_POINT_UNLOCK (76),
QUEST_COND_SCENE_LEVEL_TAG_EQ (77);
private final int value;
QuestCondType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,82 @@
package emu.grasscutter.game.quest.enums;
public enum QuestExecType {
QUEST_EXEC_NONE (0),
QUEST_EXEC_DEL_PACK_ITEM (1),
QUEST_EXEC_UNLOCK_POINT (2),
QUEST_EXEC_UNLOCK_AREA (3),
QUEST_EXEC_UNLOCK_FORCE (4),
QUEST_EXEC_LOCK_FORCE (5),
QUEST_EXEC_CHANGE_AVATAR_ELEMET (6),
QUEST_EXEC_REFRESH_GROUP_MONSTER (7),
QUEST_EXEC_SET_IS_FLYABLE (8),
QUEST_EXEC_SET_IS_WEATHER_LOCKED (9),
QUEST_EXEC_SET_IS_GAME_TIME_LOCKED (10),
QUEST_EXEC_SET_IS_TRANSFERABLE (11),
QUEST_EXEC_GRANT_TRIAL_AVATAR (12),
QUEST_EXEC_OPEN_BORED (13),
QUEST_EXEC_ROLLBACK_QUEST (14),
QUEST_EXEC_NOTIFY_GROUP_LUA (15),
QUEST_EXEC_SET_OPEN_STATE (16),
QUEST_EXEC_LOCK_POINT (17),
QUEST_EXEC_DEL_PACK_ITEM_BATCH (18),
QUEST_EXEC_REFRESH_GROUP_SUITE (19),
QUEST_EXEC_REMOVE_TRIAL_AVATAR (20),
QUEST_EXEC_SET_GAME_TIME (21),
QUEST_EXEC_SET_WEATHER_GADGET (22),
QUEST_EXEC_ADD_QUEST_PROGRESS (23),
QUEST_EXEC_NOTIFY_DAILY_TASK (24),
QUEST_EXEC_CREATE_PATTERN_GROUP (25),
QUEST_EXEC_REMOVE_PATTERN_GROUP (26),
QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM (27),
QUEST_EXEC_ACTIVE_ITEM_GIVING (28),
QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM (29),
QUEST_EXEC_ROLLBACK_PARENT_QUEST (30),
QUEST_EXEC_LOCK_AVATAR_TEAM (31),
QUEST_EXEC_UNLOCK_AVATAR_TEAM (32),
QUEST_EXEC_UPDATE_PARENT_QUEST_REWARD_INDEX (33),
QUEST_EXEC_SET_DAILY_TASK_VAR (34),
QUEST_EXEC_INC_DAILY_TASK_VAR (35),
QUEST_EXEC_DEC_DAILY_TASK_VAR (36),
QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE (37),
QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE (38),
QUEST_EXEC_ADD_CUR_AVATAR_ENERGY (39),
QUEST_EXEC_START_BARGAIN (41),
QUEST_EXEC_STOP_BARGAIN (42),
QUEST_EXEC_SET_QUEST_GLOBAL_VAR (43),
QUEST_EXEC_INC_QUEST_GLOBAL_VAR (44),
QUEST_EXEC_DEC_QUEST_GLOBAL_VAR (45),
QUEST_EXEC_REGISTER_DYNAMIC_GROUP (46),
QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP (47),
QUEST_EXEC_SET_QUEST_VAR (48),
QUEST_EXEC_INC_QUEST_VAR (49),
QUEST_EXEC_DEC_QUEST_VAR (50),
QUEST_EXEC_RANDOM_QUEST_VAR (51),
QUEST_EXEC_ACTIVATE_SCANNING_PIC (52),
QUEST_EXEC_RELOAD_SCENE_TAG (53),
QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY (54),
QUEST_EXEC_CHANGE_SKILL_DEPOT (55),
QUEST_EXEC_ADD_SCENE_TAG (56),
QUEST_EXEC_DEL_SCENE_TAG (57),
QUEST_EXEC_INIT_TIME_VAR (58),
QUEST_EXEC_CLEAR_TIME_VAR (59),
QUEST_EXEC_MODIFY_CLIMATE_AREA (60),
QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM (61),
QUEST_EXEC_CHANGE_MAP_AREA_STATE (62),
QUEST_EXEC_DEACTIVE_ITEM_GIVING (63),
QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG (64),
QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE (65),
QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE (66),
QUEST_EXEC_FAIL_MAINCOOP (67),
QUEST_EXEC_MODIFY_WEATHER_AREA (68);
private final int value;
QuestExecType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,17 @@
package emu.grasscutter.game.quest.enums;
public enum QuestGuideType {
QUEST_GUIDE_NONE (0),
QUEST_GUIDE_LOCATION (1),
QUEST_GUIDE_NPC (2);
private final int value;
QuestGuideType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,16 @@
package emu.grasscutter.game.quest.enums;
public enum QuestShowType {
QUEST_SHOW (0),
QUEST_HIDDEN (1);
private final int value;
QuestShowType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,19 @@
package emu.grasscutter.game.quest.enums;
public enum QuestState {
QUEST_STATE_NONE (0),
QUEST_STATE_UNSTARTED (1),
QUEST_STATE_UNFINISHED (2),
QUEST_STATE_FINISHED (3),
QUEST_STATE_FAILED (4);
private final int value;
QuestState(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,22 @@
package emu.grasscutter.game.quest.enums;
public enum QuestType {
AQ (0),
FQ (1),
LQ (2),
EQ (3),
DQ (4),
IQ (5),
VQ (6),
WQ (7);
private final int value;
QuestType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,17 @@
package emu.grasscutter.game.quest.enums;
public enum ShowQuestGuideType {
QUEST_GUIDE_ITEM_ENABLE (0),
QUEST_GUIDE_ITEM_DISABLE (1),
QUEST_GUIDE_ITEM_MOVE_HIDE (2);
private final int value;
ShowQuestGuideType(int id) {
this.value = id;
}
public int getValue() {
return value;
}
}

View File

@ -0,0 +1,22 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.FinishedParentQuestNotifyOuterClass.FinishedParentQuestNotify;
public class PacketFinishedParentQuestNotify extends BasePacket {
public PacketFinishedParentQuestNotify(Player player) {
super(PacketOpcodes.FinishedParentQuestNotify, true);
FinishedParentQuestNotify.Builder proto = FinishedParentQuestNotify.newBuilder();
for (GameMainQuest mainQuest : player.getQuestManager().getQuests().values()) {
proto.addParentQuestList(mainQuest.toProto());
}
this.setData(proto);
}
}

View File

@ -0,0 +1,19 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.FinishedParentQuestUpdateNotifyOuterClass.FinishedParentQuestUpdateNotify;
public class PacketFinishedParentQuestUpdateNotify extends BasePacket {
public PacketFinishedParentQuestUpdateNotify(GameMainQuest quest) {
super(PacketOpcodes.FinishedParentQuestUpdateNotify);
FinishedParentQuestUpdateNotify proto = FinishedParentQuestUpdateNotify.newBuilder()
.addParentQuestList(quest.toProto())
.build();
this.setData(proto);
}
}

View File

@ -0,0 +1,23 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.QuestManager;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.QuestListNotifyOuterClass.QuestListNotify;
public class PacketQuestListNotify extends BasePacket {
public PacketQuestListNotify(Player player) {
super(PacketOpcodes.QuestListNotify, true);
QuestListNotify.Builder proto = QuestListNotify.newBuilder();
player.getQuestManager().forEachQuest(quest -> {
proto.addQuestList(quest.toProto());
});
this.setData(proto);
}
}

View File

@ -0,0 +1,20 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.QuestListUpdateNotifyOuterClass.QuestListUpdateNotify;
public class PacketQuestListUpdateNotify extends BasePacket {
public PacketQuestListUpdateNotify(GameQuest quest) {
super(PacketOpcodes.QuestListUpdateNotify);
QuestListUpdateNotify proto = QuestListUpdateNotify.newBuilder()
.addQuestList(quest.toProto())
.build();
this.setData(proto);
}
}

View File

@ -0,0 +1,30 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.QuestProgressUpdateNotifyOuterClass.QuestProgressUpdateNotify;
public class PacketQuestProgressUpdateNotify extends BasePacket {
public PacketQuestProgressUpdateNotify(GameQuest quest) {
super(PacketOpcodes.QuestProgressUpdateNotify);
QuestProgressUpdateNotify.Builder proto = QuestProgressUpdateNotify.newBuilder().setQuestId(quest.getQuestId());
if (quest.getFinishProgressList() != null) {
for (int i : quest.getFinishProgressList()) {
proto.addFinishProgressList(i);
}
}
if (quest.getFailProgressList() != null) {
for (int i : quest.getFailProgressList()) {
proto.addFailProgressList(i);
}
}
this.setData(proto);
}
}

View File

@ -0,0 +1,37 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.QuestConfig;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.quest.GameMainQuest;
import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.ServerCondMeetQuestListUpdateNotifyOuterClass.ServerCondMeetQuestListUpdateNotify;
public class PacketServerCondMeetQuestListUpdateNotify extends BasePacket {
public PacketServerCondMeetQuestListUpdateNotify(Player player) {
super(PacketOpcodes.ServerCondMeetQuestListUpdateNotify);
ServerCondMeetQuestListUpdateNotify.Builder proto = ServerCondMeetQuestListUpdateNotify.newBuilder();
player.getQuestManager().forEachQuest(quest -> {
if (quest.getState().getValue() <= 2) {
proto.addAddQuestIdList(quest.getQuestId());
}
});
this.setData(proto);
}
public PacketServerCondMeetQuestListUpdateNotify(GameQuest quest) {
super(PacketOpcodes.ServerCondMeetQuestListUpdateNotify);
ServerCondMeetQuestListUpdateNotify proto = ServerCondMeetQuestListUpdateNotify.newBuilder()
.addAddQuestIdList(quest.getQuestId())
.build();
this.setData(proto);
}
}

View File

@ -210,6 +210,14 @@
"success": "Coordinates: %s, %s, %s\nScene id: %s",
"description": "Get coordinates."
},
"quest": {
"description": "Add or finish quests",
"usage": "quest <add|finish> [quest id]",
"added": "Quest %s added",
"finished": "Finished quest %s",
"not_found": "Quest not found",
"invalid_id": "Invalid quest id"
},
"reload": {
"reload_start": "Reloading config.",
"reload_done": "Reload complete.",