mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 12:05:39 +00:00
Implement group-based item giving & Add content handler for item giving
This commit is contained in:
parent
afc5841596
commit
40bbfd90e1
@ -3,6 +3,7 @@ package emu.grasscutter;
|
|||||||
public final class DebugConstants {
|
public final class DebugConstants {
|
||||||
public static boolean LOG_ABILITIES = false;
|
public static boolean LOG_ABILITIES = false;
|
||||||
public static boolean LOG_LUA_SCRIPTS = false;
|
public static boolean LOG_LUA_SCRIPTS = false;
|
||||||
|
public static boolean LOG_QUEST_START = false;
|
||||||
|
|
||||||
private DebugConstants() {
|
private DebugConstants() {
|
||||||
// Prevent instantiation.
|
// Prevent instantiation.
|
||||||
|
@ -30,11 +30,13 @@ public final class GivingData extends GameResource {
|
|||||||
private GiveType giveType;
|
private GiveType giveType;
|
||||||
|
|
||||||
public enum GiveMethod {
|
public enum GiveMethod {
|
||||||
@SerializedName("GIVING_METHOD_NONE") NONE,
|
GIVING_METHOD_NONE,
|
||||||
@SerializedName("GIVING_METHOD_EXACT") EXACT,
|
/** All items are required to succeed. */
|
||||||
@SerializedName("GIVING_METHOD_GROUP") GROUP,
|
GIVING_METHOD_EXACT,
|
||||||
@SerializedName("GIVING_METHOD_VAGUE_GROUP") GROUP_VAGUE,
|
GIVING_METHOD_GROUP,
|
||||||
@SerializedName("GIVING_METHOD_ANY_NO_FINISH") ANY
|
/** One in the group is required to succeed. */
|
||||||
|
GIVING_METHOD_VAGUE_GROUP,
|
||||||
|
GIVING_METHOD_ANY_NO_FINISH
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GiveType {
|
public enum GiveType {
|
||||||
|
@ -59,6 +59,19 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
this.getInventoryTypes().put(type.getValue(), tab);
|
this.getInventoryTypes().put(type.getValue(), tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first item in the inventory with the given item id.
|
||||||
|
*
|
||||||
|
* @param itemId The item id to search for.
|
||||||
|
* @return The first item found with the given item id, or null if no item was
|
||||||
|
*/
|
||||||
|
public GameItem getFirstItem(int itemId) {
|
||||||
|
return this.getItems().values().stream()
|
||||||
|
.filter(item -> item.getItemId() == itemId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
public GameItem getItemByGuid(long id) {
|
public GameItem getItemByGuid(long id) {
|
||||||
return this.getItems().get(id);
|
return this.getItems().get(id);
|
||||||
}
|
}
|
||||||
@ -155,25 +168,25 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
* Checks to see if the player has the item in their inventory.
|
* Checks to see if the player has the item in their inventory.
|
||||||
* This will succeed if the player has at least the minimum count of the item.
|
* This will succeed if the player has at least the minimum count of the item.
|
||||||
*
|
*
|
||||||
* @param itemGuid The item id to check for.
|
* @param itemId The item id to check for.
|
||||||
* @param minCount The minimum count of the item to check for.
|
* @param minCount The minimum count of the item to check for.
|
||||||
* @return True if the player has the item, false otherwise.
|
* @return True if the player has the item, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasItem(long itemGuid, int minCount) {
|
public boolean hasItem(int itemId, int minCount) {
|
||||||
return hasItem(itemGuid, minCount, false);
|
return hasItem(itemId, minCount, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if the player has the item in their inventory.
|
* Checks to see if the player has the item in their inventory.
|
||||||
*
|
*
|
||||||
* @param itemGuid The item id to check for.
|
* @param itemId The item id to check for.
|
||||||
* @param count The count of the item to check for.
|
* @param count The count of the item to check for.
|
||||||
* @param enforce If true, the player must have the exact amount.
|
* @param enforce If true, the player must have the exact amount.
|
||||||
* If false, the player must have at least the amount.
|
* If false, the player must have at least the amount.
|
||||||
* @return True if the player has the item, false otherwise.
|
* @return True if the player has the item, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasItem(long itemGuid, int count, boolean enforce) {
|
public boolean hasItem(int itemId, int count, boolean enforce) {
|
||||||
var item = this.getItemByGuid(itemGuid);
|
var item = this.getFirstItem(itemId);
|
||||||
if (item == null) return false;
|
if (item == null) return false;
|
||||||
|
|
||||||
return enforce ?
|
return enforce ?
|
||||||
@ -188,9 +201,9 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
* @param items A map of item game IDs to their count.
|
* @param items A map of item game IDs to their count.
|
||||||
* @return True if the player has the items, false otherwise.
|
* @return True if the player has the items, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean hasAllItems(Map<Long, Integer> items) {
|
public boolean hasAllItems(Collection<ItemParam> items) {
|
||||||
for (var item : items.entrySet()) {
|
for (var item : items) {
|
||||||
if (!this.hasItem(item.getKey(), item.getValue(), true))
|
if (!this.hasItem(item.getItemId(), item.getCount(), true))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,9 +499,9 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
*
|
*
|
||||||
* @param items A map of item game IDs to the amount of items to remove.
|
* @param items A map of item game IDs to the amount of items to remove.
|
||||||
*/
|
*/
|
||||||
public void removeItems(Map<Long, Integer> items) {
|
public void removeItems(Collection<ItemParam> items) {
|
||||||
for (var entry : items.entrySet()) {
|
for (var entry : items) {
|
||||||
this.removeItem(entry.getKey(), entry.getValue());
|
this.removeItem(entry.getItemId(), entry.getCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,6 +509,25 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
return removeItem(guid, 1);
|
return removeItem(guid, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an item from the player's inventory.
|
||||||
|
* This uses the item ID to find the first stack of the item's type.
|
||||||
|
*
|
||||||
|
* @param itemId The ID of the item to remove.
|
||||||
|
* @param count The amount of items to remove.
|
||||||
|
* @return True if the item was removed, false otherwise.
|
||||||
|
*/
|
||||||
|
public synchronized boolean removeItem(int itemId, int count) {
|
||||||
|
var item = this.getItems().values().stream()
|
||||||
|
.filter(i -> i.getItemId() == itemId)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
// Check if the item is in the player's inventory.
|
||||||
|
return item
|
||||||
|
.filter(gameItem -> this.removeItem(gameItem, count))
|
||||||
|
.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized boolean removeItem(long guid, int count) {
|
public synchronized boolean removeItem(long guid, int count) {
|
||||||
var item = this.getItemByGuid(guid);
|
var item = this.getItemByGuid(guid);
|
||||||
|
|
||||||
@ -514,10 +546,14 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
|||||||
* @return True if the item was removed, false otherwise.
|
* @return True if the item was removed, false otherwise.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean removeItemById(int itemId, int count) {
|
public synchronized boolean removeItemById(int itemId, int count) {
|
||||||
var item = this.getItems().values().stream().filter(i -> i.getItemId() == itemId).findFirst();
|
var item = this.getItems().values().stream()
|
||||||
|
.filter(i -> i.getItemId() == itemId)
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
// Check if the item is in the player's inventory.
|
// Check if the item is in the player's inventory.
|
||||||
return item.filter(gameItem -> this.removeItem(gameItem, count)).isPresent();
|
return item
|
||||||
|
.filter(gameItem -> this.removeItem(gameItem, count))
|
||||||
|
.isPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean removeItem(GameItem item) {
|
public synchronized boolean removeItem(GameItem item) {
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
package emu.grasscutter.game.player;
|
package emu.grasscutter.game.player;
|
||||||
|
|
||||||
import dev.morphia.annotations.Entity;
|
import dev.morphia.annotations.*;
|
||||||
import dev.morphia.annotations.Transient;
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.game.quest.ItemGiveRecord;
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import lombok.*;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
/** Tracks progress the player made in the world, like obtained items, seen characters and more */
|
/** Tracks progress the player made in the world, like obtained items, seen characters and more */
|
||||||
|
@Getter
|
||||||
@Entity
|
@Entity
|
||||||
public class PlayerProgress {
|
public class PlayerProgress {
|
||||||
@Getter @Setter @Transient private Player player;
|
@Setter @Transient private Player player;
|
||||||
|
private Map<Integer, ItemEntry> itemHistory;
|
||||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A list of dungeon IDs which have been completed.
|
* A list of dungeon IDs which have been completed.
|
||||||
* This only applies to one-time dungeons.
|
* This only applies to one-time dungeons.
|
||||||
*/
|
*/
|
||||||
@Getter private IntArrayList completedDungeons;
|
private IntArrayList completedDungeons;
|
||||||
|
|
||||||
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
// keep track of EXEC_ADD_QUEST_PROGRESS count, will be used in CONTENT_ADD_QUEST_PROGRESS
|
||||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||||
// it will be hard to loop and compare
|
// it will be hard to loop and compare
|
||||||
private Map<String, Integer> questProgressCountMap;
|
private Map<String, Integer> questProgressCountMap;
|
||||||
|
|
||||||
|
private Map<Integer, ItemGiveRecord> itemGivings;
|
||||||
|
|
||||||
public PlayerProgress() {
|
public PlayerProgress() {
|
||||||
this.questProgressCountMap = new ConcurrentHashMap<>();
|
this.questProgressCountMap = new ConcurrentHashMap<>();
|
||||||
this.completedDungeons = new IntArrayList();
|
this.completedDungeons = new IntArrayList();
|
||||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||||
|
this.itemGivings = new Int2ObjectOpenHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package emu.grasscutter.game.quest;
|
package emu.grasscutter.game.quest;
|
||||||
|
|
||||||
import dev.morphia.annotations.*;
|
import dev.morphia.annotations.*;
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.*;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.*;
|
import emu.grasscutter.data.excels.*;
|
||||||
import emu.grasscutter.data.excels.quest.QuestData;
|
import emu.grasscutter.data.excels.quest.QuestData;
|
||||||
@ -15,10 +15,11 @@ import emu.grasscutter.scripts.data.SceneGroup;
|
|||||||
import emu.grasscutter.server.packet.send.*;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import emu.grasscutter.utils.Utils;
|
import emu.grasscutter.utils.Utils;
|
||||||
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
|
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
|
||||||
import java.util.*;
|
|
||||||
import javax.script.Bindings;
|
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
|
import javax.script.Bindings;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class GameQuest {
|
public class GameQuest {
|
||||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||||
@ -104,7 +105,10 @@ public class GameQuest {
|
|||||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||||
this.getOwner().getQuestManager().checkQuestAlreadyFulfilled(this);
|
this.getOwner().getQuestManager().checkQuestAlreadyFulfilled(this);
|
||||||
|
|
||||||
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
if (DebugConstants.LOG_QUEST_START) {
|
||||||
|
Grasscutter.getLogger().debug("Quest {} is started", subQuestId);
|
||||||
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
75
src/main/java/emu/grasscutter/game/quest/ItemGiveRecord.java
Normal file
75
src/main/java/emu/grasscutter/game/quest/ItemGiveRecord.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package emu.grasscutter.game.quest;
|
||||||
|
|
||||||
|
import dev.morphia.annotations.Entity;
|
||||||
|
import emu.grasscutter.data.GameData;
|
||||||
|
import emu.grasscutter.data.excels.giving.GivingData.GiveMethod;
|
||||||
|
import emu.grasscutter.net.proto.GivingRecordOuterClass.GivingRecord;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Entity
|
||||||
|
@Builder
|
||||||
|
public final class ItemGiveRecord {
|
||||||
|
/**
|
||||||
|
* Provides a builder for an item give record.
|
||||||
|
* Uses information from game resources.
|
||||||
|
*
|
||||||
|
* @param givingId The ID of the giving action.
|
||||||
|
* @return A builder for an item give record.
|
||||||
|
*/
|
||||||
|
public static ItemGiveRecord resolve(int givingId) {
|
||||||
|
var givingData = GameData.getGivingDataMap().get(givingId);
|
||||||
|
if (givingData == null) throw new RuntimeException("No giving data found for " + givingId + ".");
|
||||||
|
|
||||||
|
var builder = ItemGiveRecord.builder()
|
||||||
|
.givingId(givingId)
|
||||||
|
.finished(false);
|
||||||
|
|
||||||
|
// Create a map.
|
||||||
|
var givenItems = new HashMap<Integer, Integer>();
|
||||||
|
if (givingData.getGivingMethod() == GiveMethod.GIVING_METHOD_EXACT) {
|
||||||
|
givingData.getExactItems().forEach(item ->
|
||||||
|
givenItems.put(item.getItemId(), 0));
|
||||||
|
} else {
|
||||||
|
givingData.getGivingGroupIds().forEach(groupId -> {
|
||||||
|
var groupData = GameData.getGivingGroupDataMap().get((int) groupId);
|
||||||
|
if (groupData == null) return;
|
||||||
|
|
||||||
|
// Add all items in the group.
|
||||||
|
groupData.getItemIds().forEach(itemId ->
|
||||||
|
givenItems.put(itemId, 0));
|
||||||
|
builder.groupId(groupId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder
|
||||||
|
.givenItems(givenItems)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int givingId;
|
||||||
|
private int configId;
|
||||||
|
|
||||||
|
private int groupId;
|
||||||
|
private int lastGroupId;
|
||||||
|
|
||||||
|
private boolean finished;
|
||||||
|
private Map<Integer, Integer> givenItems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A serialized protobuf object.
|
||||||
|
*/
|
||||||
|
public GivingRecord toProto() {
|
||||||
|
return GivingRecord.newBuilder()
|
||||||
|
.setGivingId(this.getGivingId())
|
||||||
|
.setConfigId(this.getConfigId())
|
||||||
|
.setGroupId(this.getGroupId())
|
||||||
|
.setLastGroupId(this.getLastGroupId())
|
||||||
|
.setIsFinished(this.isFinished())
|
||||||
|
.setIsGadgetGiving(false)
|
||||||
|
.putAllMaterialCntMap(this.getGivenItems())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
@ -2,33 +2,26 @@ package emu.grasscutter.game.quest;
|
|||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.binout.MainQuestData;
|
import emu.grasscutter.data.binout.*;
|
||||||
import emu.grasscutter.data.binout.ScenePointEntry;
|
|
||||||
import emu.grasscutter.data.excels.quest.QuestData;
|
import emu.grasscutter.data.excels.quest.QuestData;
|
||||||
import emu.grasscutter.database.DatabaseHelper;
|
import emu.grasscutter.database.DatabaseHelper;
|
||||||
import emu.grasscutter.game.player.BasePlayerManager;
|
import emu.grasscutter.game.player.*;
|
||||||
import emu.grasscutter.game.player.Player;
|
|
||||||
import emu.grasscutter.game.quest.enums.*;
|
import emu.grasscutter.game.quest.enums.*;
|
||||||
import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify;
|
|
||||||
import emu.grasscutter.server.packet.send.PacketQuestGlobalVarNotify;
|
|
||||||
import emu.grasscutter.game.world.Position;
|
import emu.grasscutter.game.world.Position;
|
||||||
|
import emu.grasscutter.net.proto.GivingRecordOuterClass.GivingRecord;
|
||||||
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||||
import it.unimi.dsi.fastutil.ints.*;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import lombok.Getter;
|
import lombok.*;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static emu.grasscutter.GameConstants.DEBUG;
|
import static emu.grasscutter.GameConstants.DEBUG;
|
||||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
import static emu.grasscutter.config.Configuration.*;
|
||||||
import static emu.grasscutter.config.Configuration.SERVER;
|
|
||||||
|
|
||||||
public final class QuestManager extends BasePlayerManager {
|
public final class QuestManager extends BasePlayerManager {
|
||||||
@Getter private final Player player;
|
@Getter private final Player player;
|
||||||
@ -36,52 +29,13 @@ public final class QuestManager extends BasePlayerManager {
|
|||||||
@Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
|
@Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
|
||||||
@Getter private final List<Integer> loggedQuests;
|
@Getter private final List<Integer> loggedQuests;
|
||||||
|
|
||||||
@Getter private final List<Integer> activeGivings = new ArrayList<>();
|
|
||||||
|
|
||||||
private long lastHourCheck = 0;
|
private long lastHourCheck = 0;
|
||||||
private long lastDayCheck = 0;
|
private long lastDayCheck = 0;
|
||||||
|
|
||||||
public static final ExecutorService eventExecutor;
|
public static final ExecutorService eventExecutor
|
||||||
static {
|
= new ThreadPoolExecutor(4, 4,
|
||||||
eventExecutor = new ThreadPoolExecutor(4, 4,
|
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
||||||
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
||||||
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
On SetPlayerBornDataReq, the server sends FinishedParentQuestNotify, with this exact
|
|
||||||
parentQuestList. Captured on Game version 2.7
|
|
||||||
Note: quest 40063 is already set to finished, with childQuest 4006406's state set to 3
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static Set<Integer> newPlayerMainQuests = Set.of(303,318,348,349,350,351,416,500,
|
|
||||||
501,502,503,504,505,506,507,508,509,20000,20507,20509,21004,21005,21010,21011,21016,21017,
|
|
||||||
21020,21021,21025,40063,70121,70124,70511,71010,71012,71013,71015,71016,71017,71555);
|
|
||||||
|
|
||||||
/*
|
|
||||||
On SetPlayerBornDataReq, the server sends ServerCondMeetQuestListUpdateNotify, with this exact
|
|
||||||
addQuestIdList. Captured on Game version 2.7
|
|
||||||
Total of 161...
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
private static Set<Integer> newPlayerServerCondMeetQuestListUpdateNotify = Set.of(3100101, 7104405, 2201601,
|
|
||||||
7100801, 1907002, 7293301, 7193801, 7293401, 7193901, 7091001, 7190501, 7090901, 7190401, 7090801, 7190301,
|
|
||||||
7195301, 7294801, 7195201, 7293001, 7094001, 7193501, 7293501, 7194001, 7293701, 7194201, 7194301, 7293801,
|
|
||||||
7194901, 7194101, 7195001, 7294501, 7294101, 7194601, 7294301, 7194801, 7091301, 7290301, 2102401, 7216801,
|
|
||||||
7190201, 7090701, 7093801, 7193301, 7292801, 7227828, 7093901, 7193401, 7292901, 7093701, 7193201, 7292701,
|
|
||||||
7082402, 7093601, 7292601, 7193101, 2102301, 7093501, 7292501, 7193001, 7093401, 7292401, 7192901, 7093301,
|
|
||||||
7292301, 7192801, 7294201, 7194701, 2100301, 7093201, 7212402, 7292201, 7192701, 7280001, 7293901, 7194401,
|
|
||||||
7093101, 7212302, 7292101, 7192601, 7093001, 7292001, 7192501, 7216001, 7195101, 7294601, 2100900, 7092901,
|
|
||||||
7291901, 7192401, 7092801, 7291801, 7192301, 2101501, 7092701, 7291701, 7192201, 7106401, 2100716, 7091801,
|
|
||||||
7290801, 7191301, 7293201, 7193701, 7094201, 7294001, 7194501, 2102290, 7227829, 7193601, 7094101, 7091401,
|
|
||||||
7290401, 7190901, 7106605, 7291601, 7192101, 7092601, 7291501, 7192001, 7092501, 7291401, 7191901, 7092401,
|
|
||||||
7291301, 7191801, 7092301, 7211402, 7291201, 7191701, 7092201, 7291101, 7191601, 7092101, 7291001, 7191501,
|
|
||||||
7092001, 7290901, 7191401, 7091901, 7290701, 7191201, 7091701, 7290601, 7191101, 7091601, 7290501, 7191001,
|
|
||||||
7091501, 7290201, 7190701, 7091201, 7190601, 7091101, 7190101, 7090601, 7090501, 7090401, 7010701, 7090301,
|
|
||||||
7090201, 7010103, 7090101
|
|
||||||
);
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static long getQuestKey(int mainQuestId) {
|
public static long getQuestKey(int mainQuestId) {
|
||||||
QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
|
QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
|
||||||
@ -137,20 +91,99 @@ public final class QuestManager extends BasePlayerManager {
|
|||||||
return GAME_OPTIONS.questing.enabled;
|
return GAME_OPTIONS.questing.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPlayerBorn() {
|
/**
|
||||||
// TODO scan the quest and start the quest with acceptCond fulfilled
|
* Attempts to add the giving action.
|
||||||
// The off send 3 request in that order:
|
*
|
||||||
// 1. FinishedParentQuestNotify
|
* @param givingId The giving action ID.
|
||||||
// 2. QuestListNotify
|
* @throws IllegalStateException If the giving action is already active.
|
||||||
// 3. ServerCondMeetQuestListUpdateNotify
|
*/
|
||||||
|
public void addGiveItemAction(int givingId)
|
||||||
|
throws IllegalStateException {
|
||||||
|
var progress = this.player.getPlayerProgress();
|
||||||
|
var givings = progress.getItemGivings();
|
||||||
|
|
||||||
if (this.isQuestingEnabled()) {
|
// Check if the action is already present.
|
||||||
this.enableQuests();
|
if (givings.containsKey(givingId)) {
|
||||||
|
throw new IllegalStateException("Giving action " + givingId + " is already active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
// Add the action.
|
||||||
// this.getPlayer().sendPacket(new PacketQuestListNotify(subQuests));
|
givings.put(givingId, ItemGiveRecord.resolve(givingId));
|
||||||
// this.getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
|
// Save the givings.
|
||||||
|
player.save();
|
||||||
|
|
||||||
|
this.sendGivingRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks a giving action as completed.
|
||||||
|
*
|
||||||
|
* @param givingId The giving action ID.
|
||||||
|
*/
|
||||||
|
public void markCompleted(int givingId) {
|
||||||
|
var progress = this.player.getPlayerProgress();
|
||||||
|
var givings = progress.getItemGivings();
|
||||||
|
|
||||||
|
// Check if the action is already present.
|
||||||
|
if (!givings.containsKey(givingId)) {
|
||||||
|
throw new IllegalStateException("Giving action " + givingId + " is not active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the action as finished.
|
||||||
|
givings.get(givingId).setFinished(true);
|
||||||
|
// Save the givings.
|
||||||
|
player.save();
|
||||||
|
|
||||||
|
this.sendGivingRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to remove the giving action.
|
||||||
|
*
|
||||||
|
* @param givingId The giving action ID.
|
||||||
|
*/
|
||||||
|
public void removeGivingItemAction(int givingId) {
|
||||||
|
var progress = this.player.getPlayerProgress();
|
||||||
|
var givings = progress.getItemGivings();
|
||||||
|
|
||||||
|
// Check if the action is already present.
|
||||||
|
if (!givings.containsKey(givingId)) {
|
||||||
|
throw new IllegalStateException("Giving action " + givingId + " is not active.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the action.
|
||||||
|
givings.remove(givingId);
|
||||||
|
// Save the givings.
|
||||||
|
player.save();
|
||||||
|
|
||||||
|
this.sendGivingRecords();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Serialized giving records to be used in a packet.
|
||||||
|
*/
|
||||||
|
public Collection<GivingRecord> getGivingRecords() {
|
||||||
|
return this.getPlayer().getPlayerProgress()
|
||||||
|
.getItemGivings().values().stream()
|
||||||
|
.map(ItemGiveRecord::toProto)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to remove the giving action.
|
||||||
|
*/
|
||||||
|
public void sendGivingRecords() {
|
||||||
|
// Send the record to the player.
|
||||||
|
this.player.sendPacket(
|
||||||
|
new PacketGivingRecordNotify(
|
||||||
|
this.getGivingRecords()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPlayerBorn() {
|
||||||
|
if (this.isQuestingEnabled()) {
|
||||||
|
this.enableQuests();
|
||||||
|
this.sendGivingRecords();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onLogin() {
|
public void onLogin() {
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.game.quest.content;
|
||||||
|
|
||||||
|
import emu.grasscutter.data.excels.quest.QuestData;
|
||||||
|
import emu.grasscutter.game.quest.*;
|
||||||
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
|
|
||||||
|
@QuestValueContent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING)
|
||||||
|
public final class ContentFinishGivingItem extends BaseContent {
|
||||||
|
@Override
|
||||||
|
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||||
|
var giveAction = quest.getOwner()
|
||||||
|
.getPlayerProgress()
|
||||||
|
.getItemGivings()
|
||||||
|
.get(condition.getParam()[0]);
|
||||||
|
return giveAction != null && giveAction.isFinished();
|
||||||
|
}
|
||||||
|
}
|
@ -10,17 +10,20 @@ import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
|||||||
public final class ExecActiveItemGiving extends QuestExecHandler {
|
public final class ExecActiveItemGiving extends QuestExecHandler {
|
||||||
@Override
|
@Override
|
||||||
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
|
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
|
||||||
var questManager = quest.getOwner().getQuestManager();
|
var player = quest.getOwner();
|
||||||
var activeGivings = questManager.getActiveGivings();
|
var questManager = player.getQuestManager();
|
||||||
|
|
||||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||||
if (activeGivings.contains(givingId)) {
|
try {
|
||||||
Grasscutter.getLogger().debug("Quest {} attempted to add give action {} twice.",
|
questManager.addGiveItemAction(givingId);
|
||||||
|
|
||||||
|
Grasscutter.getLogger().debug("Quest {} added give action {}.",
|
||||||
|
quest.getSubQuestId(), givingId);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalStateException ignored) {
|
||||||
|
Grasscutter.getLogger().warn("Quest {} attempted to add give action {} twice.",
|
||||||
quest.getSubQuestId(), givingId);
|
quest.getSubQuestId(), givingId);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
activeGivings.add(givingId);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,15 @@ public final class ExecDeactivateItemGiving extends QuestExecHandler {
|
|||||||
@Override
|
@Override
|
||||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||||
var questManager = quest.getOwner().getQuestManager();
|
var questManager = quest.getOwner().getQuestManager();
|
||||||
var activeGivings = questManager.getActiveGivings();
|
|
||||||
|
|
||||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||||
if (!activeGivings.contains(givingId)) {
|
try {
|
||||||
Grasscutter.getLogger().debug("Quest {} attempted to remove give action {} when it isn't active.",
|
questManager.removeGivingItemAction(givingId);
|
||||||
|
return true;
|
||||||
|
} catch (IllegalStateException ignored) {
|
||||||
|
Grasscutter.getLogger().warn("Quest {} attempted to remove give action {} twice.",
|
||||||
quest.getSubQuestId(), givingId);
|
quest.getSubQuestId(), givingId);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
activeGivings.remove(givingId);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package emu.grasscutter.server.packet.recv;
|
|||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.giving.GivingData.GiveMethod;
|
|
||||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||||
import emu.grasscutter.net.packet.*;
|
import emu.grasscutter.net.packet.*;
|
||||||
import emu.grasscutter.net.proto.ItemGivingReqOuterClass.ItemGivingReq;
|
import emu.grasscutter.net.proto.ItemGivingReqOuterClass.ItemGivingReq;
|
||||||
@ -10,6 +9,8 @@ import emu.grasscutter.server.game.GameSession;
|
|||||||
import emu.grasscutter.server.packet.send.PacketItemGivingRsp;
|
import emu.grasscutter.server.packet.send.PacketItemGivingRsp;
|
||||||
import emu.grasscutter.server.packet.send.PacketItemGivingRsp.Mode;
|
import emu.grasscutter.server.packet.send.PacketItemGivingRsp.Mode;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Opcodes(PacketOpcodes.ItemGivingReq)
|
@Opcodes(PacketOpcodes.ItemGivingReq)
|
||||||
public final class HandlerItemGivingReq extends PacketHandler {
|
public final class HandlerItemGivingReq extends PacketHandler {
|
||||||
@Override
|
@Override
|
||||||
@ -20,38 +21,83 @@ public final class HandlerItemGivingReq extends PacketHandler {
|
|||||||
var inventory = player.getInventory();
|
var inventory = player.getInventory();
|
||||||
|
|
||||||
var giveId = req.getGivingId();
|
var giveId = req.getGivingId();
|
||||||
var items = req.getItemGuidCountMapMap();
|
var items = req.getItemParamListList();
|
||||||
|
|
||||||
switch (req.getItemGivingType()) {
|
switch (req.getItemGivingType()) {
|
||||||
case QUEST -> {
|
case QUEST -> {
|
||||||
var questManager = player.getQuestManager();
|
var questManager = player.getQuestManager();
|
||||||
var activeGivings = questManager.getActiveGivings();
|
var activeGivings = player.getPlayerProgress().getItemGivings();
|
||||||
if (!activeGivings.contains(giveId)) return;
|
if (!activeGivings.containsKey(giveId)) return;
|
||||||
|
|
||||||
// Check the items against the resources.
|
// Check the items against the resources.
|
||||||
var data = GameData.getGivingDataMap().get(giveId);
|
var data = GameData.getGivingDataMap().get(giveId);
|
||||||
if (data == null) throw new IllegalArgumentException("No giving data found for " + giveId + ".");
|
if (data == null) throw new IllegalArgumentException("No giving data found for " + giveId + ".");
|
||||||
|
|
||||||
if (data.getGivingMethod() == GiveMethod.EXACT) {
|
switch (data.getGivingMethod()) {
|
||||||
if (!inventory.hasAllItems(items)) {
|
case GIVING_METHOD_EXACT -> {
|
||||||
player.sendPacket(new PacketItemGivingRsp());
|
// Check if the player has all the items.
|
||||||
return;
|
if (!inventory.hasAllItems(items)) {
|
||||||
}
|
player.sendPacket(new PacketItemGivingRsp());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove the items if the quest specifies.
|
// Remove the items if the quest specifies.
|
||||||
if (data.isRemoveItem()) {
|
if (data.isRemoveItem()) {
|
||||||
inventory.removeItems(items);
|
inventory.removeItems(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the response packet.
|
// Send the response packet.
|
||||||
player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS));
|
player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS));
|
||||||
// Remove the action from the active givings.
|
// Remove the action from the active givings.
|
||||||
activeGivings.remove(giveId);
|
questManager.removeGivingItemAction(giveId);
|
||||||
// Queue the content action.
|
// Queue the content action.
|
||||||
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId);
|
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0);
|
||||||
} else {
|
}
|
||||||
// TODO: Handle group givings.
|
case GIVING_METHOD_VAGUE_GROUP -> {
|
||||||
player.sendPacket(new PacketItemGivingRsp());
|
var matchedGroups = new ArrayList<Integer>();
|
||||||
|
var givenItems = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
|
// Resolve potential item IDs.
|
||||||
|
var groupData = GameData.getGivingGroupDataMap();
|
||||||
|
data.getGivingGroupIds().stream()
|
||||||
|
.map(groupId -> groupData.get((int) groupId))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(group -> {
|
||||||
|
var itemIds = group.getItemIds();
|
||||||
|
|
||||||
|
// Match item stacks to the group items.
|
||||||
|
items.forEach(param -> {
|
||||||
|
// Get the item instance.
|
||||||
|
var itemInstance = inventory.getFirstItem(param.getItemId());
|
||||||
|
if (itemInstance == null) return;
|
||||||
|
|
||||||
|
// Get the item ID.
|
||||||
|
var itemId = itemInstance.getItemId();
|
||||||
|
if (!itemIds.contains(itemId)) return;
|
||||||
|
|
||||||
|
// Add the item to the given items.
|
||||||
|
givenItems.put(itemId, param.getCount());
|
||||||
|
matchedGroups.add(group.getId());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the player has any items.
|
||||||
|
if (givenItems.isEmpty() && matchedGroups.isEmpty()) {
|
||||||
|
player.sendPacket(new PacketItemGivingRsp());
|
||||||
|
} else {
|
||||||
|
// Remove the items if the quest specifies.
|
||||||
|
if (data.isRemoveItem()) {
|
||||||
|
inventory.removeItems(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the response packet.
|
||||||
|
player.sendPacket(new PacketItemGivingRsp(matchedGroups.get(0), Mode.GROUP_SUCCESS));
|
||||||
|
// Mark the giving action as completed.
|
||||||
|
questManager.markCompleted(giveId);
|
||||||
|
// Queue the content action.
|
||||||
|
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case GADGET -> {
|
case GADGET -> {
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.*;
|
||||||
|
import emu.grasscutter.net.proto.GivingRecordNotifyOuterClass.GivingRecordNotify;
|
||||||
|
import emu.grasscutter.net.proto.GivingRecordOuterClass.GivingRecord;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public final class PacketGivingRecordNotify extends BasePacket {
|
||||||
|
public PacketGivingRecordNotify(Collection<GivingRecord> records) {
|
||||||
|
super(PacketOpcodes.GivingRecordNotify);
|
||||||
|
|
||||||
|
this.setData(GivingRecordNotify.newBuilder()
|
||||||
|
.addAllGivingRecordList(records)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user