mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 13:15: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 static boolean LOG_ABILITIES = false;
|
||||
public static boolean LOG_LUA_SCRIPTS = false;
|
||||
public static boolean LOG_QUEST_START = false;
|
||||
|
||||
private DebugConstants() {
|
||||
// Prevent instantiation.
|
||||
|
@ -30,11 +30,13 @@ public final class GivingData extends GameResource {
|
||||
private GiveType giveType;
|
||||
|
||||
public enum GiveMethod {
|
||||
@SerializedName("GIVING_METHOD_NONE") NONE,
|
||||
@SerializedName("GIVING_METHOD_EXACT") EXACT,
|
||||
@SerializedName("GIVING_METHOD_GROUP") GROUP,
|
||||
@SerializedName("GIVING_METHOD_VAGUE_GROUP") GROUP_VAGUE,
|
||||
@SerializedName("GIVING_METHOD_ANY_NO_FINISH") ANY
|
||||
GIVING_METHOD_NONE,
|
||||
/** All items are required to succeed. */
|
||||
GIVING_METHOD_EXACT,
|
||||
GIVING_METHOD_GROUP,
|
||||
/** One in the group is required to succeed. */
|
||||
GIVING_METHOD_VAGUE_GROUP,
|
||||
GIVING_METHOD_ANY_NO_FINISH
|
||||
}
|
||||
|
||||
public enum GiveType {
|
||||
|
@ -59,6 +59,19 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
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) {
|
||||
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.
|
||||
* 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.
|
||||
* @return True if the player has the item, false otherwise.
|
||||
*/
|
||||
public boolean hasItem(long itemGuid, int minCount) {
|
||||
return hasItem(itemGuid, minCount, false);
|
||||
public boolean hasItem(int itemId, int minCount) {
|
||||
return hasItem(itemId, minCount, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 enforce If true, the player must have the exact amount.
|
||||
* If false, the player must have at least the amount.
|
||||
* @return True if the player has the item, false otherwise.
|
||||
*/
|
||||
public boolean hasItem(long itemGuid, int count, boolean enforce) {
|
||||
var item = this.getItemByGuid(itemGuid);
|
||||
public boolean hasItem(int itemId, int count, boolean enforce) {
|
||||
var item = this.getFirstItem(itemId);
|
||||
if (item == null) return false;
|
||||
|
||||
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.
|
||||
* @return True if the player has the items, false otherwise.
|
||||
*/
|
||||
public boolean hasAllItems(Map<Long, Integer> items) {
|
||||
for (var item : items.entrySet()) {
|
||||
if (!this.hasItem(item.getKey(), item.getValue(), true))
|
||||
public boolean hasAllItems(Collection<ItemParam> items) {
|
||||
for (var item : items) {
|
||||
if (!this.hasItem(item.getItemId(), item.getCount(), true))
|
||||
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.
|
||||
*/
|
||||
public void removeItems(Map<Long, Integer> items) {
|
||||
for (var entry : items.entrySet()) {
|
||||
this.removeItem(entry.getKey(), entry.getValue());
|
||||
public void removeItems(Collection<ItemParam> items) {
|
||||
for (var entry : items) {
|
||||
this.removeItem(entry.getItemId(), entry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,6 +509,25 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
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) {
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
return item.filter(gameItem -> this.removeItem(gameItem, count)).isPresent();
|
||||
return item
|
||||
.filter(gameItem -> this.removeItem(gameItem, count))
|
||||
.isPresent();
|
||||
}
|
||||
|
||||
public synchronized boolean removeItem(GameItem item) {
|
||||
|
@ -1,40 +1,40 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import dev.morphia.annotations.*;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.quest.ItemGiveRecord;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Map;
|
||||
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 */
|
||||
@Getter
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
@Getter @Setter @Transient private Player player;
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
@Setter @Transient private Player player;
|
||||
private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
/*
|
||||
* A list of dungeon IDs which have been completed.
|
||||
* 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
|
||||
// not sure where to put this, this should be saved to DB but not to individual quest, since
|
||||
// it will be hard to loop and compare
|
||||
private Map<String, Integer> questProgressCountMap;
|
||||
|
||||
private Map<Integer, ItemGiveRecord> itemGivings;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new ConcurrentHashMap<>();
|
||||
this.completedDungeons = new IntArrayList();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
this.itemGivings = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.*;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.*;
|
||||
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.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
|
||||
import java.util.*;
|
||||
import javax.script.Bindings;
|
||||
import lombok.*;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import java.util.*;
|
||||
|
||||
@Entity
|
||||
public class GameQuest {
|
||||
@Transient @Getter @Setter private GameMainQuest mainQuest;
|
||||
@ -104,7 +105,10 @@ public class GameQuest {
|
||||
.forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
|
||||
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();
|
||||
}
|
||||
|
||||
|
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.data.GameData;
|
||||
import emu.grasscutter.data.binout.MainQuestData;
|
||||
import emu.grasscutter.data.binout.ScenePointEntry;
|
||||
import emu.grasscutter.data.binout.*;
|
||||
import emu.grasscutter.data.excels.quest.QuestData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.player.BasePlayerManager;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.player.*;
|
||||
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.net.proto.GivingRecordOuterClass.GivingRecord;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import lombok.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static emu.grasscutter.GameConstants.DEBUG;
|
||||
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
import static emu.grasscutter.config.Configuration.SERVER;
|
||||
import static emu.grasscutter.config.Configuration.*;
|
||||
|
||||
public final class QuestManager extends BasePlayerManager {
|
||||
@Getter private final Player player;
|
||||
@ -36,52 +29,13 @@ public final class QuestManager extends BasePlayerManager {
|
||||
@Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
|
||||
@Getter private final List<Integer> loggedQuests;
|
||||
|
||||
@Getter private final List<Integer> activeGivings = new ArrayList<>();
|
||||
|
||||
private long lastHourCheck = 0;
|
||||
private long lastDayCheck = 0;
|
||||
|
||||
public static final ExecutorService eventExecutor;
|
||||
static {
|
||||
eventExecutor = new ThreadPoolExecutor(4, 4,
|
||||
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
||||
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
||||
}
|
||||
|
||||
/*
|
||||
On SetPlayerBornDataReq, the server sends FinishedParentQuestNotify, with this exact
|
||||
parentQuestList. Captured on Game version 2.7
|
||||
Note: quest 40063 is already set to finished, with childQuest 4006406's state set to 3
|
||||
*/
|
||||
|
||||
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 final ExecutorService eventExecutor
|
||||
= new ThreadPoolExecutor(4, 4,
|
||||
60, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000),
|
||||
FastThreadLocalThread::new, new ThreadPoolExecutor.AbortPolicy());
|
||||
|
||||
public static long getQuestKey(int mainQuestId) {
|
||||
QuestEncryptionKey questEncryptionKey = GameData.getMainQuestEncryptionMap().get(mainQuestId);
|
||||
@ -137,20 +91,99 @@ public final class QuestManager extends BasePlayerManager {
|
||||
return GAME_OPTIONS.questing.enabled;
|
||||
}
|
||||
|
||||
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
|
||||
/**
|
||||
* Attempts to add the giving action.
|
||||
*
|
||||
* @param givingId The giving action ID.
|
||||
* @throws IllegalStateException If the giving action is already active.
|
||||
*/
|
||||
public void addGiveItemAction(int givingId)
|
||||
throws IllegalStateException {
|
||||
var progress = this.player.getPlayerProgress();
|
||||
var givings = progress.getItemGivings();
|
||||
|
||||
if (this.isQuestingEnabled()) {
|
||||
this.enableQuests();
|
||||
// Check if the action is already present.
|
||||
if (givings.containsKey(givingId)) {
|
||||
throw new IllegalStateException("Giving action " + givingId + " is already active.");
|
||||
}
|
||||
|
||||
// this.getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(newQuests));
|
||||
// this.getPlayer().sendPacket(new PacketQuestListNotify(subQuests));
|
||||
// this.getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
|
||||
// Add the action.
|
||||
givings.put(givingId, ItemGiveRecord.resolve(givingId));
|
||||
// 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() {
|
||||
|
@ -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 {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
|
||||
var questManager = quest.getOwner().getQuestManager();
|
||||
var activeGivings = questManager.getActiveGivings();
|
||||
var player = quest.getOwner();
|
||||
var questManager = player.getQuestManager();
|
||||
|
||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||
if (activeGivings.contains(givingId)) {
|
||||
Grasscutter.getLogger().debug("Quest {} attempted to add give action {} twice.",
|
||||
try {
|
||||
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);
|
||||
return false;
|
||||
} else {
|
||||
activeGivings.add(givingId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,15 @@ public final class ExecDeactivateItemGiving extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
var questManager = quest.getOwner().getQuestManager();
|
||||
var activeGivings = questManager.getActiveGivings();
|
||||
|
||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||
if (!activeGivings.contains(givingId)) {
|
||||
Grasscutter.getLogger().debug("Quest {} attempted to remove give action {} when it isn't active.",
|
||||
try {
|
||||
questManager.removeGivingItemAction(givingId);
|
||||
return true;
|
||||
} catch (IllegalStateException ignored) {
|
||||
Grasscutter.getLogger().warn("Quest {} attempted to remove give action {} twice.",
|
||||
quest.getSubQuestId(), givingId);
|
||||
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.data.GameData;
|
||||
import emu.grasscutter.data.excels.giving.GivingData.GiveMethod;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
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.Mode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Opcodes(PacketOpcodes.ItemGivingReq)
|
||||
public final class HandlerItemGivingReq extends PacketHandler {
|
||||
@Override
|
||||
@ -20,38 +21,83 @@ public final class HandlerItemGivingReq extends PacketHandler {
|
||||
var inventory = player.getInventory();
|
||||
|
||||
var giveId = req.getGivingId();
|
||||
var items = req.getItemGuidCountMapMap();
|
||||
var items = req.getItemParamListList();
|
||||
|
||||
switch (req.getItemGivingType()) {
|
||||
case QUEST -> {
|
||||
var questManager = player.getQuestManager();
|
||||
var activeGivings = questManager.getActiveGivings();
|
||||
if (!activeGivings.contains(giveId)) return;
|
||||
var activeGivings = player.getPlayerProgress().getItemGivings();
|
||||
if (!activeGivings.containsKey(giveId)) return;
|
||||
|
||||
// Check the items against the resources.
|
||||
var data = GameData.getGivingDataMap().get(giveId);
|
||||
if (data == null) throw new IllegalArgumentException("No giving data found for " + giveId + ".");
|
||||
|
||||
if (data.getGivingMethod() == GiveMethod.EXACT) {
|
||||
if (!inventory.hasAllItems(items)) {
|
||||
player.sendPacket(new PacketItemGivingRsp());
|
||||
return;
|
||||
}
|
||||
switch (data.getGivingMethod()) {
|
||||
case GIVING_METHOD_EXACT -> {
|
||||
// Check if the player has all the items.
|
||||
if (!inventory.hasAllItems(items)) {
|
||||
player.sendPacket(new PacketItemGivingRsp());
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the items if the quest specifies.
|
||||
if (data.isRemoveItem()) {
|
||||
inventory.removeItems(items);
|
||||
}
|
||||
// Remove the items if the quest specifies.
|
||||
if (data.isRemoveItem()) {
|
||||
inventory.removeItems(items);
|
||||
}
|
||||
|
||||
// Send the response packet.
|
||||
player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS));
|
||||
// Remove the action from the active givings.
|
||||
activeGivings.remove(giveId);
|
||||
// Queue the content action.
|
||||
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId);
|
||||
} else {
|
||||
// TODO: Handle group givings.
|
||||
player.sendPacket(new PacketItemGivingRsp());
|
||||
// Send the response packet.
|
||||
player.sendPacket(new PacketItemGivingRsp(giveId, Mode.EXACT_SUCCESS));
|
||||
// Remove the action from the active givings.
|
||||
questManager.removeGivingItemAction(giveId);
|
||||
// Queue the content action.
|
||||
questManager.queueEvent(QuestContent.QUEST_CONTENT_FINISH_ITEM_GIVING, giveId, 0);
|
||||
}
|
||||
case GIVING_METHOD_VAGUE_GROUP -> {
|
||||
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 -> {
|
||||
|
@ -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