mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-23 07:33:02 +08:00
Merge branch 'talking' into dev-4.0
This commit is contained in:
commit
70eb0f2b16
11
src/main/java/emu/grasscutter/DebugConstants.java
Normal file
11
src/main/java/emu/grasscutter/DebugConstants.java
Normal file
@ -0,0 +1,11 @@
|
||||
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.
|
||||
}
|
||||
}
|
@ -4,58 +4,33 @@ import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.binout.*;
|
||||
import emu.grasscutter.data.binout.config.*;
|
||||
import emu.grasscutter.data.binout.routes.Route;
|
||||
import emu.grasscutter.data.custom.TrialAvatarActivityCustomData;
|
||||
import emu.grasscutter.data.custom.TrialAvatarCustomData;
|
||||
import emu.grasscutter.data.custom.*;
|
||||
import emu.grasscutter.data.excels.*;
|
||||
import emu.grasscutter.data.excels.achievement.AchievementData;
|
||||
import emu.grasscutter.data.excels.achievement.AchievementGoalData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityCondExcelConfigData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityShopData;
|
||||
import emu.grasscutter.data.excels.activity.ActivityWatcherData;
|
||||
import emu.grasscutter.data.excels.achievement.*;
|
||||
import emu.grasscutter.data.excels.activity.*;
|
||||
import emu.grasscutter.data.excels.avatar.*;
|
||||
import emu.grasscutter.data.excels.codex.*;
|
||||
import emu.grasscutter.data.excels.dungeon.*;
|
||||
import emu.grasscutter.data.excels.monster.MonsterCurveData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterDescribeData;
|
||||
import emu.grasscutter.data.excels.monster.MonsterSpecialNameData;
|
||||
import emu.grasscutter.data.excels.quest.QuestData;
|
||||
import emu.grasscutter.data.excels.quest.QuestGlobalVarData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryLevelData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
|
||||
import emu.grasscutter.data.excels.reliquary.ReliquarySetData;
|
||||
import emu.grasscutter.data.excels.tower.TowerFloorData;
|
||||
import emu.grasscutter.data.excels.tower.TowerLevelData;
|
||||
import emu.grasscutter.data.excels.tower.TowerScheduleData;
|
||||
import emu.grasscutter.data.excels.giving.*;
|
||||
import emu.grasscutter.data.excels.monster.*;
|
||||
import emu.grasscutter.data.excels.quest.*;
|
||||
import emu.grasscutter.data.excels.reliquary.*;
|
||||
import emu.grasscutter.data.excels.tower.*;
|
||||
import emu.grasscutter.data.excels.trial.*;
|
||||
import emu.grasscutter.data.excels.weapon.WeaponCurveData;
|
||||
import emu.grasscutter.data.excels.weapon.WeaponLevelData;
|
||||
import emu.grasscutter.data.excels.weapon.WeaponPromoteData;
|
||||
import emu.grasscutter.data.excels.world.WeatherData;
|
||||
import emu.grasscutter.data.excels.world.WorldAreaData;
|
||||
import emu.grasscutter.data.excels.world.WorldLevelData;
|
||||
import emu.grasscutter.data.server.ActivityCondGroup;
|
||||
import emu.grasscutter.data.server.DropSubfieldMapping;
|
||||
import emu.grasscutter.data.server.DropTableExcelConfigData;
|
||||
import emu.grasscutter.data.server.GadgetMapping;
|
||||
import emu.grasscutter.data.server.MonsterMapping;
|
||||
import emu.grasscutter.data.server.SubfieldMapping;
|
||||
import emu.grasscutter.data.excels.weapon.*;
|
||||
import emu.grasscutter.data.excels.world.*;
|
||||
import emu.grasscutter.data.server.*;
|
||||
import emu.grasscutter.game.dungeons.DungeonDropEntry;
|
||||
import emu.grasscutter.game.quest.QuestEncryptionKey;
|
||||
import emu.grasscutter.game.quest.RewindData;
|
||||
import emu.grasscutter.game.quest.TeleportData;
|
||||
import emu.grasscutter.game.quest.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestCond;
|
||||
import emu.grasscutter.game.world.GroupReplacementData;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import lombok.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@SuppressWarnings({"unused", "MismatchedQueryAndUpdateOfCollection"})
|
||||
public final class GameData {
|
||||
@ -165,6 +140,10 @@ public final class GameData {
|
||||
private static final Int2ObjectMap<AvatarTalentData> avatarTalentDataMap =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<BargainData> bargainDataMap
|
||||
= new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<BattlePassMissionData> battlePassMissionDataMap =
|
||||
new Int2ObjectOpenHashMap<>();
|
||||
@ -266,6 +245,12 @@ public final class GameData {
|
||||
@Getter
|
||||
private static final Int2ObjectMap<GatherData> gatherDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<GivingData> givingDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
private static final Int2ObjectMap<GivingGroupData> givingGroupDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Getter
|
||||
@Deprecated // This is to prevent people from using this map. This is for the resource loader
|
||||
// only!
|
||||
|
49
src/main/java/emu/grasscutter/data/excels/BargainData.java
Normal file
49
src/main/java/emu/grasscutter/data/excels/BargainData.java
Normal file
@ -0,0 +1,49 @@
|
||||
package emu.grasscutter.data.excels;
|
||||
|
||||
import emu.grasscutter.data.*;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@ResourceType(name = "BargainExcelConfigData.json")
|
||||
public final class BargainData extends GameResource {
|
||||
@Getter private int id;
|
||||
private int questId;
|
||||
|
||||
private List<Integer> dialogId;
|
||||
|
||||
/**
|
||||
* This is a list of 2 integers.
|
||||
* The first integer is the minimum value of the bargain.
|
||||
* The second integer is the maximum value of the bargain.
|
||||
*/
|
||||
private List<Integer> expectedValue;
|
||||
private int space;
|
||||
|
||||
private List<Integer> successTalkId;
|
||||
private int failTalkId;
|
||||
private int moodNpcId;
|
||||
|
||||
/**
|
||||
* This is a list of 2 integers.
|
||||
* The first integer is the minimum value of the mood.
|
||||
* The second integer is the maximum value of the mood.
|
||||
*/
|
||||
private List<Integer> randomMood;
|
||||
private int moodAlertLimit;
|
||||
private int moodLowLimit;
|
||||
private int singleFailMoodDeduction;
|
||||
|
||||
private long moodLowLimitTextTextMapHash;
|
||||
private long titleTextTextMapHash;
|
||||
private long affordTextTextMapHash;
|
||||
private long storageTextTextMapHash;
|
||||
private long moodHintTextTextMapHash;
|
||||
private long moodDescTextTextMapHash;
|
||||
|
||||
private List<Integer> singleFailTalkId;
|
||||
|
||||
private boolean deleteItem;
|
||||
private int itemId;
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package emu.grasscutter.data.excels.giving;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.*;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.game.inventory.BagTab;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ResourceType(name = "GivingExcelConfigData.json")
|
||||
public final class GivingData extends GameResource {
|
||||
@SerializedName(value = "id", alternate = "Id")
|
||||
private int id;
|
||||
private int talkId;
|
||||
private int mistakeTalkId;
|
||||
|
||||
private BagTab tab;
|
||||
private GiveMethod givingMethod;
|
||||
|
||||
private List<ItemParamData> exactItems;
|
||||
private int exactFinishTalkId;
|
||||
|
||||
private List<Integer> givingGroupIds;
|
||||
private int givingGroupCount;
|
||||
|
||||
private boolean isRemoveItem;
|
||||
private GiveType giveType;
|
||||
|
||||
public enum GiveMethod {
|
||||
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 {
|
||||
@SerializedName("GIVING_TYPE_QUEST") QUEST,
|
||||
@SerializedName("GIVING_TYPE_GROUP") GROUP
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package emu.grasscutter.data.excels.giving;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import emu.grasscutter.data.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@ResourceType(name = "GivingGroupExcelConfigData.json")
|
||||
public final class GivingGroupData extends GameResource {
|
||||
@SerializedName(value = "id", alternate = "Id")
|
||||
private int id;
|
||||
|
||||
@SerializedName(value = "itemIds", alternate = "ItemIds")
|
||||
private List<Integer> itemIds;
|
||||
|
||||
private int finishTalkId;
|
||||
private int mistakeTalkId;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.ability;
|
||||
|
||||
import com.google.protobuf.*;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.*;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
@ -19,13 +19,13 @@ import emu.grasscutter.net.proto.AbilityScalarTypeOuterClass.AbilityScalarType;
|
||||
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
|
||||
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||
import io.netty.util.concurrent.FastThreadLocalThread;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.*;
|
||||
import lombok.Getter;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
public final class AbilityManager extends BasePlayerManager {
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public final class AbilityManager extends BasePlayerManager {
|
||||
private static final HashMap<AbilityModifierAction.Type, AbilityActionHandler> actionHandlers =
|
||||
new HashMap<>();
|
||||
private static final HashMap<AbilityMixinData.Type, AbilityMixinHandler> mixinHandlers =
|
||||
@ -91,8 +91,11 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
Ability ability, AbilityModifierAction action, ByteString abilityData, GameEntity target) {
|
||||
var handler = actionHandlers.get(action.type);
|
||||
if (handler == null || ability == null) {
|
||||
Grasscutter.getLogger()
|
||||
if (DebugConstants.LOG_ABILITIES) {
|
||||
Grasscutter.getLogger()
|
||||
.debug("Could not execute ability action {} at {}", action.type, ability);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -148,7 +151,7 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
invoke.getArgumentType(),
|
||||
invoke.getArgumentTypeValue(),
|
||||
entity.getId());
|
||||
} else {
|
||||
} else if (DebugConstants.LOG_ABILITIES) {
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"Invoke type of {} ({}) has no entity. (referring to {})",
|
||||
@ -375,7 +378,10 @@ public final class AbilityManager extends BasePlayerManager {
|
||||
|
||||
var entity = this.player.getScene().getEntityById(invoke.getEntityId());
|
||||
if (entity == null) {
|
||||
Grasscutter.getLogger().debug("Entity not found: {}", invoke.getEntityId());
|
||||
if (DebugConstants.LOG_ABILITIES) {
|
||||
Grasscutter.getLogger().debug("Entity not found: {}", invoke.getEntityId());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package emu.grasscutter.game.ability.actions;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction.DropType;
|
||||
@ -81,8 +81,11 @@ public final class ActionGenerateElemBall extends AbilityActionHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
Grasscutter.getLogger()
|
||||
.debug("Generating {} of {} element balls", amountGenerated, action.configID);
|
||||
if (DebugConstants.LOG_ABILITIES) {
|
||||
Grasscutter.getLogger()
|
||||
.debug("Generating {} of {} element balls", amountGenerated, action.configID);
|
||||
}
|
||||
|
||||
for (int i = 0; i < amountGenerated; i++) {
|
||||
EntityItem energyBall =
|
||||
new EntityItem(
|
||||
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.game.ability.actions;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.game.ability.Ability;
|
||||
import emu.grasscutter.game.entity.EntityClientGadget;
|
||||
@ -22,12 +22,14 @@ public final class ActionHealHP extends AbilityActionHandler {
|
||||
ownerGadget
|
||||
.getScene()
|
||||
.getEntityById(ownerGadget.getOwnerEntityId()); // Caster for EntityClientGadget
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"Owner {} has top owner {}: {}",
|
||||
ability.getOwner(),
|
||||
ownerGadget.getOwnerEntityId(),
|
||||
owner);
|
||||
if (DebugConstants.LOG_ABILITIES) {
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"Owner {} has top owner {}: {}",
|
||||
ability.getOwner(),
|
||||
ownerGadget.getOwnerEntityId(),
|
||||
owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (owner == null) return false;
|
||||
@ -76,7 +78,7 @@ public final class ActionHealHP extends AbilityActionHandler {
|
||||
amountToRegenerate +=
|
||||
amountByTargetMaxHPRatio * target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
|
||||
Grasscutter.getLogger().debug(">>> Healing {} without ratios", amountToRegenerate);
|
||||
Grasscutter.getLogger().trace("Healing {} without ratios", amountToRegenerate);
|
||||
target.heal(
|
||||
amountToRegenerate * abilityRatio * action.healRatio.get(ability, 1f),
|
||||
action.muteHealEffect);
|
||||
|
55
src/main/java/emu/grasscutter/game/inventory/BagTab.java
Normal file
55
src/main/java/emu/grasscutter/game/inventory/BagTab.java
Normal file
@ -0,0 +1,55 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public enum BagTab {
|
||||
TAB_NONE(0),
|
||||
TAB_WEAPON(1),
|
||||
TAB_EQUIP(2),
|
||||
TAB_AVATAR(3),
|
||||
TAB_FOOD(4),
|
||||
TAB_MATERIAL(5),
|
||||
TAB_QUEST(6),
|
||||
TAB_CONSUME(7),
|
||||
TAB_WIDGET(8),
|
||||
TAB_HOMEWORLD(9);
|
||||
|
||||
private static final Int2ObjectMap<BagTab> map = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, BagTab> stringMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
Stream.of(BagTab.values())
|
||||
.forEach(
|
||||
entry -> {
|
||||
map.put(entry.getValue(), entry);
|
||||
stringMap.put(entry.name(), entry);
|
||||
});
|
||||
}
|
||||
|
||||
@Getter private final int value;
|
||||
|
||||
/**
|
||||
* Fetches the bag tab by its value.
|
||||
*
|
||||
* @param value The name of the bag tab.
|
||||
* @return The bag tab.
|
||||
*/
|
||||
public static BagTab getTypeByValue(int value) {
|
||||
return map.getOrDefault(value, TAB_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the bag tab by its name.
|
||||
*
|
||||
* @param name The name of the bag tab.
|
||||
* @return The bag tab.
|
||||
*/
|
||||
public static BagTab getTypeByName(String name) {
|
||||
return stringMap.getOrDefault(name, TAB_NONE);
|
||||
}
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package emu.grasscutter.game.inventory;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.INVENTORY_LIMITS;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
@ -17,10 +15,13 @@ import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
|
||||
import java.util.*;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.val;
|
||||
|
||||
import static emu.grasscutter.config.Configuration.INVENTORY_LIMITS;
|
||||
|
||||
public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
private final Long2ObjectMap<GameItem> store;
|
||||
private final Int2ObjectMap<InventoryTab> inventoryTypes;
|
||||
@ -60,6 +61,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);
|
||||
}
|
||||
@ -170,6 +184,52 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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(int itemId, int minCount) {
|
||||
return hasItem(itemId, minCount, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the player has the item in their inventory.
|
||||
*
|
||||
* @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(int itemId, int count, boolean enforce) {
|
||||
var item = this.getFirstItem(itemId);
|
||||
if (item == null) return false;
|
||||
|
||||
return enforce ?
|
||||
item.getCount() == count :
|
||||
item.getCount() >= count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the player has the item in their inventory.
|
||||
* This is exact.
|
||||
*
|
||||
* @param items A map of item game IDs to their count.
|
||||
* @return True if the player has the items, false otherwise.
|
||||
*/
|
||||
public boolean hasAllItems(Collection<ItemParam> items) {
|
||||
for (var item : items) {
|
||||
if (!this.hasItem(item.getItemId(), item.getCount(), true))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void triggerAddItemEvents(GameItem result) {
|
||||
try {
|
||||
getPlayer()
|
||||
@ -454,10 +514,40 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a bulk delete of items.
|
||||
*
|
||||
* @param items A map of item game IDs to the amount of items to remove.
|
||||
*/
|
||||
public void removeItems(Collection<ItemParam> items) {
|
||||
for (var entry : items) {
|
||||
this.removeItem(entry.getItemId(), entry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeItem(long guid) {
|
||||
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);
|
||||
|
||||
@ -476,10 +566,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) {
|
||||
|
@ -160,7 +160,7 @@ public class Player implements PlayerHook, FieldFetch {
|
||||
@Getter private transient FriendsList friendsList;
|
||||
@Getter private transient MailHandler mailHandler;
|
||||
@Getter private transient AbilityManager abilityManager;
|
||||
@Getter @Setter private transient QuestManager questManager;
|
||||
@Getter private transient QuestManager questManager;
|
||||
@Getter private transient TowerManager towerManager;
|
||||
@Getter private transient SotSManager sotsManager;
|
||||
@Getter private transient MapMarksManager mapMarksManager;
|
||||
|
@ -1,40 +1,42 @@
|
||||
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.*;
|
||||
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;
|
||||
private Map<Integer, BargainRecord> bargains;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new ConcurrentHashMap<>();
|
||||
this.completedDungeons = new IntArrayList();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
this.itemGivings = new Int2ObjectOpenHashMap<>();
|
||||
this.bargains = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
109
src/main/java/emu/grasscutter/game/quest/BargainRecord.java
Normal file
109
src/main/java/emu/grasscutter/game/quest/BargainRecord.java
Normal file
@ -0,0 +1,109 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.BargainData;
|
||||
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
|
||||
import emu.grasscutter.net.proto.BargainSnapshotOuterClass.BargainSnapshot;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import lombok.*;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Builder
|
||||
public final class BargainRecord {
|
||||
/**
|
||||
* Provides an instance of a bargain record.
|
||||
* Uses information from game resources.
|
||||
*
|
||||
* @param bargainId The ID of the bargain.
|
||||
* @return An instance of a bargain record.
|
||||
*/
|
||||
public static BargainRecord resolve(int bargainId) {
|
||||
var bargainData = GameData.getBargainDataMap().get(bargainId);
|
||||
if (bargainData == null) throw new RuntimeException("No bargain data found for " + bargainId + ".");
|
||||
|
||||
return BargainRecord.builder()
|
||||
.bargainId(bargainId)
|
||||
.build()
|
||||
.determineBase(bargainData);
|
||||
}
|
||||
|
||||
private int bargainId;
|
||||
private int lowestPrice;
|
||||
private int expectedPrice;
|
||||
|
||||
private int currentMood;
|
||||
|
||||
private boolean finished;
|
||||
private BargainResultType result;
|
||||
|
||||
/**
|
||||
* Determines the price of the bargain.
|
||||
*/
|
||||
public BargainRecord determineBase(BargainData data) {
|
||||
// Set the expected price.
|
||||
var price = data.getExpectedValue();
|
||||
this.setExpectedPrice(Utils.randomRange(
|
||||
price.get(0), price.get(1)));
|
||||
// Set the lowest price.
|
||||
this.setLowestPrice(price.get(0));
|
||||
|
||||
// Set the base mood.
|
||||
var mood = data.getRandomMood();
|
||||
this.setCurrentMood(Utils.randomRange(
|
||||
mood.get(0), mood.get(1)));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes an offer's validity.
|
||||
*
|
||||
* @param offer The offer to compute.
|
||||
* @return The result of the offer.
|
||||
*/
|
||||
public BargainResultType applyOffer(int offer) {
|
||||
if (offer < this.getLowestPrice()) {
|
||||
// Decrease the mood.
|
||||
this.currentMood -= Utils.randomRange(1, 3);
|
||||
// Return a failure.
|
||||
return this.result = BargainResultType.BARGAIN_SINGLE_FAIL;
|
||||
}
|
||||
|
||||
if (offer > this.getExpectedPrice()) {
|
||||
// Complete the bargain.
|
||||
this.setFinished(true);
|
||||
// Return a success.
|
||||
return this.result = BargainResultType.BARGAIN_COMPLETE_SUCC;
|
||||
}
|
||||
|
||||
// Compare the offer against the mood & expected price.
|
||||
// The mood is out of 100; 1 mood should decrease the price by 100.
|
||||
var moodAdjustment = (int) Math.floor(this.getCurrentMood() / 100.0);
|
||||
var expectedPrice = this.getExpectedPrice() - moodAdjustment;
|
||||
if (offer < expectedPrice) {
|
||||
// Decrease the mood.
|
||||
this.currentMood -= Utils.randomRange(1, 3);
|
||||
// Return a failure.
|
||||
return this.result = BargainResultType.BARGAIN_SINGLE_FAIL;
|
||||
} else {
|
||||
// Complete the bargain.
|
||||
this.setFinished(true);
|
||||
// Return a success.
|
||||
return this.result = BargainResultType.BARGAIN_COMPLETE_SUCC;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A snapshot of this bargain record.
|
||||
*/
|
||||
public BargainSnapshot toSnapshot() {
|
||||
return BargainSnapshot.newBuilder()
|
||||
.setBargainId(this.getBargainId())
|
||||
.setCurMood(this.getCurrentMood())
|
||||
.setPJHMEHGELGC(this.getExpectedPrice())
|
||||
.setHADMOPEJFIC(this.getLowestPrice())
|
||||
.build();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,84 +1,41 @@
|
||||
package emu.grasscutter.game.quest;
|
||||
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.MainQuestData;
|
||||
import emu.grasscutter.data.binout.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 class QuestManager extends BasePlayerManager {
|
||||
public final class QuestManager extends BasePlayerManager {
|
||||
@Getter private final Player player;
|
||||
|
||||
@Getter private final Int2ObjectMap<GameMainQuest> mainQuests;
|
||||
@Transient @Getter private final List<Integer> loggedQuests;
|
||||
@Getter private final List<Integer> loggedQuests;
|
||||
|
||||
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);
|
||||
@ -134,20 +91,146 @@ public 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 start the bargain.
|
||||
*
|
||||
* @param bargainId The bargain ID.
|
||||
*/
|
||||
public void startBargain(int bargainId) {
|
||||
var progress = this.player.getPlayerProgress();
|
||||
var bargains = progress.getBargains();
|
||||
|
||||
// Check if the bargain is already present.
|
||||
if (bargains.containsKey(bargainId)) {
|
||||
throw new IllegalStateException("Bargain " + bargainId + " is already active.");
|
||||
}
|
||||
|
||||
// Add the action.
|
||||
var bargain = BargainRecord.resolve(bargainId);
|
||||
bargains.put(bargainId, bargain);
|
||||
// Save the bargains.
|
||||
this.player.save();
|
||||
|
||||
// Send the player the start packet.
|
||||
this.player.sendPacket(new PacketBargainStartNotify(bargain));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to stop the bargain.
|
||||
*
|
||||
* @param bargainId The bargain ID.
|
||||
*/
|
||||
public void stopBargain(int bargainId) {
|
||||
var progress = this.player.getPlayerProgress();
|
||||
var bargains = progress.getBargains();
|
||||
|
||||
// Check if the bargain is already present.
|
||||
if (!bargains.containsKey(bargainId)) {
|
||||
throw new IllegalStateException("Bargain " + bargainId + " is not active.");
|
||||
}
|
||||
|
||||
// Remove the action.
|
||||
bargains.remove(bargainId);
|
||||
// Save the bargains.
|
||||
this.player.save();
|
||||
|
||||
// Send the player the stop packet.
|
||||
this.player.sendPacket(new PacketBargainTerminateNotify(bargainId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the giving records to the player.
|
||||
*/
|
||||
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,15 @@
|
||||
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;
|
||||
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
|
||||
|
||||
@QuestValueContent(QuestContent.QUEST_CONTENT_BARGAIN_FAIL)
|
||||
public final class ContentBargainFail extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
return condition.getParam()[0] == params[0] &&
|
||||
condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
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;
|
||||
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
|
||||
|
||||
@QuestValueContent(QuestContent.QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN)
|
||||
public final class ContentBargainLessThan extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
return condition.getParam()[0] == params[0] &&
|
||||
condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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_BARGAIN_SUCC)
|
||||
public final class ContentBargainSuccess extends BaseContent {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
|
||||
return condition.getParam()[0] == params[0] &&
|
||||
condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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) {
|
||||
return condition.getParam()[0] == params[0] &&
|
||||
condition.getParam()[1] == params[1];
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public enum QuestContent implements QuestTrigger {
|
||||
QUEST_CONTENT_ADD_QUEST_PROGRESS(24),
|
||||
QUEST_CONTENT_INTERACT_GADGET(25),
|
||||
QUEST_CONTENT_DAILY_TASK_COMP_FINISH(26), // missing, currently unused
|
||||
QUEST_CONTENT_FINISH_ITEM_GIVING(27), // missing, finish
|
||||
QUEST_CONTENT_FINISH_ITEM_GIVING(27),
|
||||
QUEST_CONTENT_SKILL(107),
|
||||
QUEST_CONTENT_CITY_LEVEL_UP(109), // missing, finish
|
||||
QUEST_CONTENT_PATTERN_GROUP_CLEAR_MONSTER(110), // missing, finish, for random quests
|
||||
@ -51,9 +51,9 @@ public enum QuestContent implements QuestTrigger {
|
||||
QUEST_CONTENT_QUEST_VAR_LESS(121),
|
||||
QUEST_CONTENT_OBTAIN_VARIOUS_ITEM(122), // missing, finish
|
||||
QUEST_CONTENT_FINISH_TOWER_LEVEL(123), // missing, currently unused
|
||||
QUEST_CONTENT_BARGAIN_SUCC(124), // missing, finish
|
||||
QUEST_CONTENT_BARGAIN_FAIL(125), // missing, fail
|
||||
QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN(126), // missing, fail
|
||||
QUEST_CONTENT_BARGAIN_SUCC(124),
|
||||
QUEST_CONTENT_BARGAIN_FAIL(125),
|
||||
QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN(126),
|
||||
QUEST_CONTENT_ACTIVITY_TRIGGER_FAILED(127), // missing, fail
|
||||
QUEST_CONTENT_MAIN_COOP_ENTER_SAVE_POINT(128), // missing, finish
|
||||
QUEST_CONTENT_ANY_MANUAL_TRANSPORT(129),
|
||||
|
@ -35,7 +35,7 @@ public enum QuestExec implements QuestTrigger {
|
||||
QUEST_EXEC_CREATE_PATTERN_GROUP(25), // missing, used for random quests
|
||||
QUEST_EXEC_REMOVE_PATTERN_GROUP(26), // missing, used for random quests
|
||||
QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM(27), // missing
|
||||
QUEST_EXEC_ACTIVE_ITEM_GIVING(28), // missing
|
||||
QUEST_EXEC_ACTIVE_ITEM_GIVING(28),
|
||||
QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM(29), // missing
|
||||
QUEST_EXEC_ROLLBACK_PARENT_QUEST(30),
|
||||
QUEST_EXEC_LOCK_AVATAR_TEAM(31), // missing
|
||||
@ -47,8 +47,8 @@ public enum QuestExec implements QuestTrigger {
|
||||
QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE(37), // missing
|
||||
QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE(38), // missing
|
||||
QUEST_EXEC_ADD_CUR_AVATAR_ENERGY(39),
|
||||
QUEST_EXEC_START_BARGAIN(41), // missing
|
||||
QUEST_EXEC_STOP_BARGAIN(42), // missing
|
||||
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),
|
||||
@ -71,7 +71,7 @@ public enum QuestExec implements QuestTrigger {
|
||||
QUEST_EXEC_MODIFY_CLIMATE_AREA(60), // missing
|
||||
QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM(61), // missing
|
||||
QUEST_EXEC_CHANGE_MAP_AREA_STATE(62), // missing
|
||||
QUEST_EXEC_DEACTIVE_ITEM_GIVING(63), // missing
|
||||
QUEST_EXEC_DEACTIVE_ITEM_GIVING(63),
|
||||
QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG(64), // missing
|
||||
QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE(65), // missing
|
||||
QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE(66), // missing
|
||||
|
@ -0,0 +1,29 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.quest.QuestData.QuestExecParam;
|
||||
import emu.grasscutter.game.quest.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_ACTIVE_ITEM_GIVING)
|
||||
public final class ExecActiveItemGiving extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestExecParam condition, String... paramStr) {
|
||||
var player = quest.getOwner();
|
||||
var questManager = player.getQuestManager();
|
||||
|
||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.quest.QuestData;
|
||||
import emu.grasscutter.game.quest.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_DEACTIVE_ITEM_GIVING)
|
||||
public final class ExecDeactivateItemGiving extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
var questManager = quest.getOwner().getQuestManager();
|
||||
|
||||
var givingId = Integer.parseInt(condition.getParam()[0]);
|
||||
try {
|
||||
questManager.removeGivingItemAction(givingId);
|
||||
return true;
|
||||
} catch (IllegalStateException ignored) {
|
||||
Grasscutter.getLogger().warn("Quest {} attempted to remove give action {} twice.",
|
||||
quest.getSubQuestId(), givingId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.quest.QuestData;
|
||||
import emu.grasscutter.game.quest.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_START_BARGAIN)
|
||||
public final class ExecStartBargain extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
// Get the bargain data from the quest parameters.
|
||||
var bargainId = Integer.parseInt(condition.getParam()[0]);
|
||||
|
||||
try {
|
||||
// Start the bargain.
|
||||
quest.getOwner().getQuestManager()
|
||||
.startBargain(bargainId);
|
||||
Grasscutter.getLogger().debug("Bargain {} started.", bargainId);
|
||||
return true;
|
||||
} catch (RuntimeException ignored) {
|
||||
Grasscutter.getLogger().debug("Bargain {} does not exist.", bargainId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package emu.grasscutter.game.quest.exec;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.excels.quest.QuestData;
|
||||
import emu.grasscutter.game.quest.*;
|
||||
import emu.grasscutter.game.quest.enums.QuestExec;
|
||||
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
|
||||
|
||||
@QuestValueExec(QuestExec.QUEST_EXEC_STOP_BARGAIN)
|
||||
public final class ExecStopBargain extends QuestExecHandler {
|
||||
@Override
|
||||
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
|
||||
// Get the bargain data from the quest parameters.
|
||||
var bargainId = Integer.parseInt(condition.getParam()[0]);
|
||||
|
||||
try {
|
||||
// Start the bargain.
|
||||
quest.getOwner().getQuestManager()
|
||||
.stopBargain(bargainId);
|
||||
Grasscutter.getLogger().debug("Bargain {} stopped.", bargainId);
|
||||
return true;
|
||||
} catch (RuntimeException ignored) {
|
||||
Grasscutter.getLogger().debug("Bargain {} does not exist.", bargainId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,12 @@
|
||||
package emu.grasscutter.scripts.data.controller;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.*;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.scripts.ScriptLib;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.CompiledScript;
|
||||
import org.luaj.vm2.LuaError;
|
||||
import org.luaj.vm2.LuaValue;
|
||||
import emu.grasscutter.scripts.*;
|
||||
import org.luaj.vm2.*;
|
||||
|
||||
import javax.script.*;
|
||||
|
||||
public class EntityController {
|
||||
private transient CompiledScript entityController;
|
||||
@ -38,9 +36,11 @@ public class EntityController {
|
||||
}
|
||||
|
||||
public int onClientExecuteRequest(GameEntity entity, int param1, int param2, int param3) {
|
||||
Grasscutter.getLogger()
|
||||
if (DebugConstants.LOG_LUA_SCRIPTS) {
|
||||
Grasscutter.getLogger()
|
||||
.debug(
|
||||
"Request on {}, {}: {}", entity.getGroupId(), param1, entity.getPosition().toString());
|
||||
"Request on {}, {}: {}", entity.getGroupId(), param1, entity.getPosition().toString());
|
||||
}
|
||||
LuaValue value =
|
||||
callControllerScriptFunc(
|
||||
entity,
|
||||
|
@ -0,0 +1,43 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.BargainOfferPriceReqOuterClass.BargainOfferPriceReq;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketBargainOfferPriceRsp;
|
||||
|
||||
@Opcodes(PacketOpcodes.BargainOfferPriceReq)
|
||||
public final class HandlerBargainOfferPriceReq extends PacketHandler {
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload)
|
||||
throws Exception {
|
||||
var packet = BargainOfferPriceReq.parseFrom(payload);
|
||||
var player = session.getPlayer();
|
||||
|
||||
// Fetch the active bargain.
|
||||
var bargainId = packet.getBargainId();
|
||||
var progress = player.getPlayerProgress();
|
||||
var bargain = progress.getBargains().get(bargainId);
|
||||
if (bargain == null) return;
|
||||
|
||||
// Apply the offer.
|
||||
var result = bargain.applyOffer(packet.getPrice());
|
||||
|
||||
// Queue the quest content event.
|
||||
var questManager = player.getQuestManager();
|
||||
switch (result) {
|
||||
case BARGAIN_COMPLETE_SUCC -> questManager.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_BARGAIN_SUCC,
|
||||
bargainId, 0);
|
||||
case BARGAIN_SINGLE_FAIL -> questManager.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN,
|
||||
bargainId, 0);
|
||||
case BARGAIN_COMPLETE_FAIL -> questManager.queueEvent(
|
||||
QuestContent.QUEST_CONTENT_BARGAIN_FAIL,
|
||||
bargainId, 0);
|
||||
}
|
||||
|
||||
// Return the resulting packet.
|
||||
session.send(new PacketBargainOfferPriceRsp(result, bargain));
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketGetAllActivatedBargainDataRsp;
|
||||
|
||||
@Opcodes(PacketOpcodes.GetAllActivatedBargainDataReq)
|
||||
public final class HandlerGetAllActivatedBargainDataReq extends PacketHandler {
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) {
|
||||
session.send(new PacketGetAllActivatedBargainDataRsp(
|
||||
session.getPlayer()
|
||||
.getPlayerProgress()
|
||||
.getBargains()
|
||||
.values()));
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.GetBargainDataReqOuterClass.GetBargainDataReq;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.game.GameSession;
|
||||
import emu.grasscutter.server.packet.send.PacketGetBargainDataRsp;
|
||||
|
||||
@Opcodes(PacketOpcodes.GetBargainDataReq)
|
||||
public final class HandlerGetBargainDataReq extends PacketHandler {
|
||||
@Override
|
||||
public void handle(GameSession session, byte[] header, byte[] payload)
|
||||
throws Exception {
|
||||
var packet = GetBargainDataReq.parseFrom(payload);
|
||||
|
||||
var bargainId = packet.getBargainId();
|
||||
var bargain = session.getPlayer()
|
||||
.getPlayerProgress()
|
||||
.getBargains()
|
||||
.get(bargainId);
|
||||
if (bargain == null) {
|
||||
session.send(new PacketGetBargainDataRsp(
|
||||
Retcode.RET_BARGAIN_NOT_ACTIVATED));
|
||||
return;
|
||||
}
|
||||
|
||||
session.send(new PacketGetBargainDataRsp(bargain));
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package emu.grasscutter.server.packet.recv;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.ItemGivingReqOuterClass.ItemGivingReq;
|
||||
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
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
var req = ItemGivingReq.parseFrom(payload);
|
||||
|
||||
var player = session.getPlayer();
|
||||
var inventory = player.getInventory();
|
||||
|
||||
var giveId = req.getGivingId();
|
||||
var items = req.getItemParamListList();
|
||||
|
||||
switch (req.getItemGivingType()) {
|
||||
case QUEST -> {
|
||||
var questManager = player.getQuestManager();
|
||||
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 + ".");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 -> {
|
||||
Grasscutter.getLogger().debug("Unimplemented gadget giving was executed for {}.", giveId);
|
||||
player.sendPacket(new PacketItemGivingRsp());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.quest.BargainRecord;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.BargainOfferPriceRspOuterClass.BargainOfferPriceRsp;
|
||||
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
|
||||
public final class PacketBargainOfferPriceRsp extends BasePacket {
|
||||
public PacketBargainOfferPriceRsp(BargainResultType result, BargainRecord record) {
|
||||
super(PacketOpcodes.BargainOfferPriceRsp);
|
||||
|
||||
this.setData(BargainOfferPriceRsp.newBuilder()
|
||||
.setRetcode(record.isFinished() ?
|
||||
Retcode.RET_BARGAIN_FINISHED.getNumber() :
|
||||
Retcode.RET_BARGAIN_NOT_ACTIVATED.getNumber())
|
||||
.setCurMood(record.getCurrentMood())
|
||||
.setBargainResult(result)
|
||||
.setResultParam(0));
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.quest.BargainRecord;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.BargainStartNotifyOuterClass.BargainStartNotify;
|
||||
|
||||
public final class PacketBargainStartNotify extends BasePacket {
|
||||
public PacketBargainStartNotify(BargainRecord record) {
|
||||
super(PacketOpcodes.BargainStartNotify);
|
||||
|
||||
this.setData(BargainStartNotify.newBuilder()
|
||||
.setBargainId(record.getBargainId())
|
||||
.setSnapshot(record.toSnapshot()));
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.BargainTerminateNotifyOuterClass.BargainTerminateNotify;
|
||||
|
||||
public final class PacketBargainTerminateNotify extends BasePacket {
|
||||
public PacketBargainTerminateNotify(int bargainId) {
|
||||
super(PacketOpcodes.BargainTerminateNotify);
|
||||
|
||||
this.setData(BargainTerminateNotify.newBuilder()
|
||||
.setBargainId(bargainId));
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.quest.BargainRecord;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.GetAllActivatedBargainDataRspOuterClass.GetAllActivatedBargainDataRsp;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public final class PacketGetAllActivatedBargainDataRsp extends BasePacket {
|
||||
public PacketGetAllActivatedBargainDataRsp(Collection<BargainRecord> records) {
|
||||
super(PacketOpcodes.GetAllActivatedBargainDataRsp);
|
||||
|
||||
this.setData(GetAllActivatedBargainDataRsp.newBuilder()
|
||||
.setRetcode(Retcode.RET_SUCC.getNumber())
|
||||
.addAllSnapshotList(records.stream()
|
||||
.map(BargainRecord::toSnapshot)
|
||||
.toList()));
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.game.quest.BargainRecord;
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.GetBargainDataRspOuterClass.GetBargainDataRsp;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
|
||||
public final class PacketGetBargainDataRsp extends BasePacket {
|
||||
public PacketGetBargainDataRsp(Retcode retcode) {
|
||||
super(PacketOpcodes.GetBargainDataRsp);
|
||||
|
||||
this.setData(GetBargainDataRsp.newBuilder()
|
||||
.setRetcode(retcode.getNumber()));
|
||||
}
|
||||
|
||||
public PacketGetBargainDataRsp(BargainRecord record) {
|
||||
super(PacketOpcodes.GetBargainDataRsp);
|
||||
|
||||
this.setData(GetBargainDataRsp.newBuilder()
|
||||
.setRetcode(Retcode.RET_SUCC.getNumber())
|
||||
.setBargainId(record.getBargainId())
|
||||
.setSnapshot(record.toSnapshot()));
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package emu.grasscutter.server.packet.send;
|
||||
|
||||
import emu.grasscutter.net.packet.*;
|
||||
import emu.grasscutter.net.proto.ItemGivingRspOuterClass.ItemGivingRsp;
|
||||
|
||||
public final class PacketItemGivingRsp extends BasePacket {
|
||||
public PacketItemGivingRsp() {
|
||||
this(0, Mode.FAILURE);
|
||||
}
|
||||
|
||||
public PacketItemGivingRsp(int value, Mode mode) {
|
||||
super(PacketOpcodes.ItemGivingRsp);
|
||||
|
||||
var packet = ItemGivingRsp.newBuilder()
|
||||
.setRetcode(mode == Mode.FAILURE ? 1 : 0);
|
||||
if (mode == Mode.EXACT_SUCCESS) {
|
||||
packet.setGivingId(value);
|
||||
} else if (mode == Mode.GROUP_SUCCESS) {
|
||||
packet.setGivingGroupId(value);
|
||||
}
|
||||
|
||||
this.setData(packet);
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
GROUP_SUCCESS,
|
||||
EXACT_SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user