mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-22 11:54:39 +01:00
Copy some files from Grasscutter-Quests
NOT completely finished, nor is it completely done. Protocol issues remain! (including lack of packet IDs)
This commit is contained in:
@@ -5,7 +5,7 @@ import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.PlayerLevelData;
|
||||
import emu.grasscutter.data.excels.WeatherData;
|
||||
import emu.grasscutter.data.excels.world.WeatherData;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.CoopRequest;
|
||||
@@ -44,6 +44,7 @@ import emu.grasscutter.game.props.ClimateType;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
import emu.grasscutter.game.quest.enums.QuestContent;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.game.shop.ShopLimit;
|
||||
import emu.grasscutter.game.tower.TowerData;
|
||||
@@ -93,235 +94,122 @@ import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
|
||||
@Entity(value = "players", useDiscriminator = false)
|
||||
public class Player {
|
||||
private transient final Int2ObjectMap<CoopRequest> coopRequests; // Synchronized getter
|
||||
@Getter
|
||||
private transient final Queue<AttackResult> attackResults;
|
||||
@Getter
|
||||
private transient final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
|
||||
@Getter
|
||||
private transient final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
|
||||
@Getter
|
||||
private transient final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||
@Id
|
||||
private int id;
|
||||
@Indexed(options = @IndexOptions(unique = true))
|
||||
private String accountId;
|
||||
@Setter
|
||||
private transient Account account;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient GameSession session;
|
||||
@Getter
|
||||
private String nickname;
|
||||
@Getter
|
||||
private String signature;
|
||||
@Getter
|
||||
private int headImage;
|
||||
@Getter
|
||||
private int nameCardId = 210001;
|
||||
@Getter
|
||||
private final Position position;
|
||||
@Getter
|
||||
@Setter
|
||||
private Position prevPos;
|
||||
@Getter
|
||||
private final Position rotation;
|
||||
@Getter
|
||||
private PlayerBirthday birthday;
|
||||
@Getter
|
||||
private PlayerCodex codex;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean showAvatars;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> showAvatarList;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> showNameCardList;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> properties;
|
||||
@Getter
|
||||
@Setter
|
||||
private int currentRealmId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int widgetId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int sceneId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int regionId;
|
||||
@Getter
|
||||
private int mainCharacterId;
|
||||
@Setter
|
||||
private boolean godmode; // Getter is inGodmode
|
||||
@Id private int id;
|
||||
@Indexed(options = @IndexOptions(unique = true)) private String accountId;
|
||||
@Setter private transient Account account;
|
||||
@Getter @Setter private transient GameSession session;
|
||||
|
||||
@Getter private String nickname;
|
||||
@Getter private String signature;
|
||||
@Getter private int headImage;
|
||||
@Getter private int nameCardId = 210001;
|
||||
@Getter private Position position;
|
||||
@Getter @Setter private Position prevPos;
|
||||
@Getter private Position rotation;
|
||||
@Getter private PlayerBirthday birthday;
|
||||
@Getter private PlayerCodex codex;
|
||||
@Getter @Setter private boolean showAvatars;
|
||||
@Getter @Setter private List<Integer> showAvatarList;
|
||||
@Getter @Setter private List<Integer> showNameCardList;
|
||||
@Getter private Map<Integer, Integer> properties;
|
||||
@Getter @Setter private int currentRealmId;
|
||||
@Getter @Setter private int widgetId;
|
||||
@Getter @Setter private int sceneId;
|
||||
@Getter @Setter private int regionId;
|
||||
@Getter private int mainCharacterId;
|
||||
@Setter private boolean godmode; // Getter is inGodmode
|
||||
private boolean stamina; // Getter is getUnlimitedStamina, Setter is setUnlimitedStamina
|
||||
@Getter
|
||||
private final Set<Integer> nameCardList;
|
||||
@Getter
|
||||
private final Set<Integer> flyCloakList;
|
||||
@Getter
|
||||
private final Set<Integer> costumeList;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> rewardedLevels;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> homeRewardedLevels;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> realmList;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Integer> seenRealmList;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedForgingBlueprints;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedCombines;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedFurniture;
|
||||
@Getter
|
||||
private final Set<Integer> unlockedFurnitureSuite;
|
||||
@Getter
|
||||
private final Map<Long, ExpeditionInfo> expeditionInfo;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> unlockedRecipies;
|
||||
@Getter
|
||||
private final List<ActiveForgeData> activeForges;
|
||||
@Getter
|
||||
private final Map<Integer, ActiveCookCompoundData> activeCookCompounds;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> questGlobalVariables;
|
||||
@Getter
|
||||
private final Map<Integer, Integer> openStates;
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<Integer, Set<Integer>> unlockedSceneAreas;
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<Integer, Set<Integer>> unlockedScenePoints;
|
||||
@Getter
|
||||
@Setter
|
||||
private List<Integer> chatEmojiIdList;
|
||||
@Transient
|
||||
private long nextGuid = 0;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private int peerId;
|
||||
@Transient
|
||||
private World world; // Synchronized getter and setter
|
||||
@Transient
|
||||
private Scene scene; // Synchronized getter and setter
|
||||
@Transient
|
||||
@Getter
|
||||
private int weatherId = 0;
|
||||
@Transient
|
||||
@Getter
|
||||
private ClimateType climate = ClimateType.CLIMATE_SUNNY;
|
||||
|
||||
@Getter private Set<Integer> nameCardList;
|
||||
@Getter private Set<Integer> flyCloakList;
|
||||
@Getter private Set<Integer> costumeList;
|
||||
@Getter @Setter private Set<Integer> rewardedLevels;
|
||||
@Getter @Setter private Set<Integer> homeRewardedLevels;
|
||||
@Getter @Setter private Set<Integer> realmList;
|
||||
@Getter @Setter private Set<Integer> seenRealmList;
|
||||
@Getter private Set<Integer> unlockedForgingBlueprints;
|
||||
@Getter private Set<Integer> unlockedCombines;
|
||||
@Getter private Set<Integer> unlockedFurniture;
|
||||
@Getter private Set<Integer> unlockedFurnitureSuite;
|
||||
@Getter private Map<Long, ExpeditionInfo> expeditionInfo;
|
||||
@Getter private Map<Integer, Integer> unlockedRecipies;
|
||||
@Getter private List<ActiveForgeData> activeForges;
|
||||
@Getter private Map<Integer, ActiveCookCompoundData> activeCookCompounds;
|
||||
@Getter private Map<Integer, Integer> questGlobalVariables;
|
||||
@Getter private Map<Integer, Integer> openStates;
|
||||
@Getter @Setter private Map<Integer, Set<Integer>> unlockedSceneAreas;
|
||||
@Getter @Setter private Map<Integer, Set<Integer>> unlockedScenePoints;
|
||||
@Getter @Setter private List<Integer> chatEmojiIdList;
|
||||
|
||||
@Transient private long nextGuid = 0;
|
||||
@Transient @Getter @Setter private int peerId;
|
||||
@Transient private World world; // Synchronized getter and setter
|
||||
@Transient private Scene scene; // Synchronized getter and setter
|
||||
@Transient @Getter private int weatherId = 0;
|
||||
@Transient @Getter private ClimateType climate = ClimateType.CLIMATE_SUNNY;
|
||||
|
||||
// Player managers go here
|
||||
@Getter
|
||||
private final transient AvatarStorage avatars;
|
||||
@Getter
|
||||
private final transient Inventory inventory;
|
||||
@Getter
|
||||
private final transient FriendsList friendsList;
|
||||
@Getter
|
||||
private final transient MailHandler mailHandler;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient MessageHandler messageHandler;
|
||||
@Getter
|
||||
private final transient AbilityManager abilityManager;
|
||||
@Getter
|
||||
@Setter
|
||||
private transient QuestManager questManager;
|
||||
@Getter
|
||||
private final transient TowerManager towerManager;
|
||||
@Getter
|
||||
private transient SotSManager sotsManager;
|
||||
@Getter
|
||||
private transient MapMarksManager mapMarksManager;
|
||||
@Getter
|
||||
private transient StaminaManager staminaManager;
|
||||
@Getter
|
||||
private transient EnergyManager energyManager;
|
||||
@Getter
|
||||
private transient ResinManager resinManager;
|
||||
@Getter
|
||||
private transient ForgingManager forgingManager;
|
||||
@Getter
|
||||
private transient DeforestationManager deforestationManager;
|
||||
@Getter
|
||||
private transient FurnitureManager furnitureManager;
|
||||
@Getter
|
||||
private transient BattlePassManager battlePassManager;
|
||||
@Getter
|
||||
private transient CookingManager cookingManager;
|
||||
@Getter
|
||||
private transient CookingCompoundManager cookingCompoundManager;
|
||||
@Getter
|
||||
private transient ActivityManager activityManager;
|
||||
@Getter
|
||||
private final transient PlayerBuffManager buffManager;
|
||||
@Getter
|
||||
private transient PlayerProgressManager progressManager;
|
||||
@Getter
|
||||
private transient SatiationManager satiationManager;
|
||||
@Getter private transient AvatarStorage avatars;
|
||||
@Getter private transient Inventory inventory;
|
||||
@Getter private transient FriendsList friendsList;
|
||||
@Getter private transient MailHandler mailHandler;
|
||||
@Getter @Setter private transient MessageHandler messageHandler;
|
||||
@Getter private transient AbilityManager abilityManager;
|
||||
@Getter @Setter private transient QuestManager questManager;
|
||||
@Getter private transient TowerManager towerManager;
|
||||
@Getter private transient SotSManager sotsManager;
|
||||
@Getter private transient MapMarksManager mapMarksManager;
|
||||
@Getter private transient StaminaManager staminaManager;
|
||||
@Getter private transient EnergyManager energyManager;
|
||||
@Getter private transient ResinManager resinManager;
|
||||
@Getter private transient ForgingManager forgingManager;
|
||||
@Getter private transient DeforestationManager deforestationManager;
|
||||
@Getter private transient FurnitureManager furnitureManager;
|
||||
@Getter private transient BattlePassManager battlePassManager;
|
||||
@Getter private transient CookingManager cookingManager;
|
||||
@Getter private transient CookingCompoundManager cookingCompoundManager;
|
||||
@Getter private transient ActivityManager activityManager;
|
||||
@Getter private transient PlayerBuffManager buffManager;
|
||||
@Getter private transient PlayerProgressManager progressManager;
|
||||
@Getter private transient SatiationManager satiationManager;
|
||||
|
||||
// Manager data (Save-able to the database)
|
||||
@Getter
|
||||
private transient Achievements achievements;
|
||||
@Getter private transient Achievements achievements;
|
||||
private PlayerProfile playerProfile; // Getter has null-check
|
||||
@Getter
|
||||
private TeamManager teamManager;
|
||||
@Getter private TeamManager teamManager;
|
||||
private TowerData towerData; // Getter has null-check
|
||||
@Getter
|
||||
private final PlayerGachaInfo gachaInfo;
|
||||
@Getter private PlayerGachaInfo gachaInfo;
|
||||
private PlayerCollectionRecords collectionRecordStore; // Getter has null-check
|
||||
@Getter
|
||||
private final ArrayList<ShopLimit> shopLimit;
|
||||
@Getter
|
||||
private transient GameHome home;
|
||||
@Setter
|
||||
private boolean moonCard; // Getter is inMoonCard
|
||||
@Getter
|
||||
@Setter
|
||||
private Date moonCardStartTime;
|
||||
@Getter
|
||||
@Setter
|
||||
private int moonCardDuration;
|
||||
@Getter
|
||||
@Setter
|
||||
private Set<Date> moonCardGetTimes;
|
||||
@Transient
|
||||
@Getter
|
||||
private boolean paused;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private int enterSceneToken;
|
||||
@Transient
|
||||
@Getter
|
||||
@Setter
|
||||
private SceneLoadState sceneLoadState = SceneLoadState.NONE;
|
||||
@Transient
|
||||
private boolean hasSentLoginPackets;
|
||||
@Transient
|
||||
private long nextSendPlayerLocTime = 0;
|
||||
@Getter
|
||||
@Setter
|
||||
private long springLastUsed;
|
||||
@Getter private ArrayList<ShopLimit> shopLimit;
|
||||
|
||||
@Getter private transient GameHome home;
|
||||
|
||||
@Setter private boolean moonCard; // Getter is inMoonCard
|
||||
@Getter @Setter private Date moonCardStartTime;
|
||||
@Getter @Setter private int moonCardDuration;
|
||||
@Getter @Setter private Set<Date> moonCardGetTimes;
|
||||
|
||||
@Transient @Getter private boolean paused;
|
||||
@Transient @Getter @Setter private int enterSceneToken;
|
||||
@Transient @Getter @Setter private SceneLoadState sceneLoadState = SceneLoadState.NONE;
|
||||
@Transient private boolean hasSentLoginPackets;
|
||||
@Transient private long nextSendPlayerLocTime = 0;
|
||||
|
||||
private transient final Int2ObjectMap<CoopRequest> coopRequests; // Synchronized getter
|
||||
@Getter private transient final Queue<AttackResult> attackResults;
|
||||
@Getter private transient final InvokeHandler<CombatInvokeEntry> combatInvokeHandler;
|
||||
@Getter private transient final InvokeHandler<AbilityInvokeEntry> abilityInvokeHandler;
|
||||
@Getter private transient final InvokeHandler<AbilityInvokeEntry> clientAbilityInitFinishHandler;
|
||||
|
||||
@Getter @Setter private long springLastUsed;
|
||||
private HashMap<String, MapMark> mapMarks; // Getter makes an empty hashmap - maybe do this elsewhere?
|
||||
@Getter
|
||||
@Setter
|
||||
private int nextResinRefresh;
|
||||
@Getter
|
||||
@Setter
|
||||
private int lastDailyReset;
|
||||
@Getter
|
||||
private final transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TODO
|
||||
@Getter @Setter private int nextResinRefresh;
|
||||
@Getter @Setter private int lastDailyReset;
|
||||
@Getter private transient MpSettingType mpSetting = MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY;
|
||||
@Getter private long playerGameTime = 0;
|
||||
|
||||
@Getter private PlayerProgress playerProgress;
|
||||
@Getter private Set<Integer> activeQuestTimers;
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||
@@ -432,6 +320,18 @@ public class Player {
|
||||
this.satiationManager = new SatiationManager(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the player's game time if it has changed.
|
||||
*
|
||||
* @param gameTime The new game time.
|
||||
*/
|
||||
public void updatePlayerGameTime(long gameTime) {
|
||||
if (this.playerGameTime == gameTime) return;
|
||||
this.playerGameTime = gameTime;
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
public int getUid() {
|
||||
return id;
|
||||
}
|
||||
@@ -717,7 +617,8 @@ public class Player {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("ENTER_REGION_" + region.config_id, true))) {
|
||||
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("ENTER_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -730,7 +631,8 @@ public class Player {
|
||||
// If trigger hasn't been fired yet
|
||||
if (!Boolean.TRUE.equals(quest.getTriggers().put("LEAVE_REGION_" + region.config_id, true))) {
|
||||
getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
|
||||
getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_TRIGGER_FIRE, quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
|
||||
getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_TRIGGER_FIRE,
|
||||
quest.getTriggerData().get("LEAVE_REGION_" + region.config_id).getId(), 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -959,6 +861,11 @@ public class Player {
|
||||
this.sendPacket(new PacketSetNameCardRsp(nameCardId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to this player.
|
||||
*
|
||||
* @param message The message to send.
|
||||
*/
|
||||
public void dropMessage(Object message) {
|
||||
if (this.messageHandler != null) {
|
||||
this.messageHandler.append(message.toString());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.excels.BuffData;
|
||||
@@ -17,13 +16,14 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import lombok.Getter;
|
||||
|
||||
public class PlayerBuffManager extends BasePlayerManager {
|
||||
public final class PlayerBuffManager extends BasePlayerManager {
|
||||
private final List<PlayerBuff> pendingBuffs;
|
||||
private final Int2ObjectMap<PlayerBuff> buffs; // Server buffs
|
||||
private int nextBuffUid;
|
||||
|
||||
public PlayerBuffManager(Player player) {
|
||||
super(player);
|
||||
|
||||
this.buffs = new Int2ObjectOpenHashMap<>();
|
||||
this.pendingBuffs = new ArrayList<>();
|
||||
}
|
||||
@@ -92,42 +92,40 @@ public class PlayerBuffManager extends BasePlayerManager {
|
||||
*/
|
||||
public synchronized boolean addBuff(int buffId, float duration, Avatar target) {
|
||||
// Get buff excel data
|
||||
BuffData buffData = GameData.getBuffDataMap().get(buffId);
|
||||
var buffData = GameData.getBuffDataMap().get(buffId);
|
||||
if (buffData == null) return false;
|
||||
|
||||
boolean success = false;
|
||||
|
||||
// Perform onAdded actions
|
||||
success |=
|
||||
var success =
|
||||
Optional.ofNullable(GameData.getAbilityData(buffData.getAbilityName()))
|
||||
.map(data -> data.modifiers.get(buffData.getModifierName()))
|
||||
.map(modifier -> modifier.onAdded)
|
||||
.map(
|
||||
onAdded -> {
|
||||
var s = false;
|
||||
for (var a : onAdded) {
|
||||
Grasscutter.getLogger().debug("onAdded exists");
|
||||
if (Objects.requireNonNull(a.type) == AbilityModifierAction.Type.HealHP) {
|
||||
Grasscutter.getLogger().debug("Attempting heal");
|
||||
var shouldHeal = false;
|
||||
for (var ability : onAdded) {
|
||||
if (Objects.requireNonNull(ability.type) == AbilityModifierAction.Type.HealHP) {
|
||||
if (target == null) continue;
|
||||
|
||||
var maxHp = target.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
var amount = a.amount.get() + a.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
var amount =
|
||||
ability.amount.get() + ability.amountByTargetMaxHPRatio.get() * maxHp;
|
||||
|
||||
target.getAsEntity().heal(amount);
|
||||
s = true;
|
||||
Grasscutter.getLogger().debug("Healed {}", amount);
|
||||
shouldHeal = true;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
return shouldHeal;
|
||||
})
|
||||
.orElse(false);
|
||||
Grasscutter.getLogger().debug("Oh no");
|
||||
|
||||
// Set duration
|
||||
if (duration < 0f) {
|
||||
duration = buffData.getTime();
|
||||
}
|
||||
|
||||
// Dont add buff if duration is equal or less than 0
|
||||
// Don't add buff if duration is equal or less than 0
|
||||
if (duration <= 0) {
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package emu.grasscutter.game.player;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.CodexAnimalData;
|
||||
import emu.grasscutter.data.excels.codex.CodexAnimalData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
|
||||
@@ -1,63 +1,60 @@
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tracks progress the player made in the world, like obtained items, seen characters and more
|
||||
*/
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// 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<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress(){
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId){
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count){
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId){
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count){
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry{
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId){
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount){
|
||||
this.obtainedCount+=amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
package emu.grasscutter.game.player;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
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 */
|
||||
@Entity
|
||||
public class PlayerProgress {
|
||||
|
||||
@Getter private Map<Integer, ItemEntry> itemHistory;
|
||||
|
||||
// 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<Integer, Integer> questProgressCountMap;
|
||||
|
||||
public PlayerProgress() {
|
||||
this.questProgressCountMap = new Int2IntOpenHashMap();
|
||||
this.itemHistory = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public boolean hasPlayerObtainedItemHistorically(int itemId) {
|
||||
return itemHistory.containsKey(itemId);
|
||||
}
|
||||
|
||||
public int addToItemHistory(int itemId, int count) {
|
||||
val itemEntry = itemHistory.computeIfAbsent(itemId, (key) -> new ItemEntry(itemId));
|
||||
return itemEntry.addToObtainedCount(count);
|
||||
}
|
||||
|
||||
public int getCurrentProgress(int progressId) {
|
||||
return questProgressCountMap.getOrDefault(progressId, -1);
|
||||
}
|
||||
|
||||
public int addToCurrentProgress(int progressId, int count) {
|
||||
return questProgressCountMap.merge(progressId, count, Integer::sum);
|
||||
}
|
||||
|
||||
@Entity
|
||||
@NoArgsConstructor
|
||||
public static class ItemEntry {
|
||||
@Getter private int itemId;
|
||||
@Getter @Setter private int obtainedCount;
|
||||
|
||||
ItemEntry(int itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
int addToObtainedCount(int amount) {
|
||||
this.obtainedCount += amount;
|
||||
return this.obtainedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import emu.grasscutter.data.excels.OpenStateData;
|
||||
import emu.grasscutter.data.excels.OpenStateData.OpenStateCondType;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.quest.enums.QuestState;
|
||||
import emu.grasscutter.game.quest.enums.QuestTrigger;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import java.util.Set;
|
||||
@@ -222,7 +221,7 @@ public class PlayerProgressManager extends BasePlayerDataManager {
|
||||
// Fire quest trigger for trans point unlock.
|
||||
this.player
|
||||
.getQuestManager()
|
||||
.triggerEvent(QuestTrigger.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
||||
.triggerEvent(QuestContent.QUEST_CONTENT_UNLOCK_TRANS_POINT, sceneId, pointId);
|
||||
|
||||
// Send packet.
|
||||
this.player.sendPacket(new PacketScenePointUnlockNotify(sceneId, pointId));
|
||||
|
||||
@@ -5,14 +5,17 @@ import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Transient;
|
||||
import emu.grasscutter.GameConstants;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
|
||||
import emu.grasscutter.data.excels.avatar.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.EntityBaseGadget;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.game.props.EnterReason;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||
@@ -20,18 +23,21 @@ import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
|
||||
import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState;
|
||||
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
|
||||
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
|
||||
import emu.grasscutter.net.proto.VisionTypeOuterClass;
|
||||
import emu.grasscutter.server.event.player.PlayerTeamDeathEvent;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.val;
|
||||
|
||||
@Entity
|
||||
public class TeamManager extends BasePlayerDataManager {
|
||||
@@ -45,8 +51,16 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
@Getter @Setter private int currentCharacterIndex;
|
||||
@Transient @Getter @Setter private TeamInfo mpTeam;
|
||||
@Transient @Getter @Setter private int entityId;
|
||||
|
||||
@Transient private int useTemporarilyTeamIndex = -1;
|
||||
@Transient private List<TeamInfo> temporaryTeam; // Temporary Team for tower
|
||||
@Transient @Getter @Setter private boolean usingTrialTeam;
|
||||
@Transient @Getter @Setter private TeamInfo trialAvatarTeam;
|
||||
// hold trial avatars for later use in rebuilding active team
|
||||
@Transient @Getter @Setter private Map<Integer, Avatar> trialAvatars;
|
||||
|
||||
@Transient @Getter @Setter
|
||||
private int previousIndex = -1; // index of character selection in team before adding trial avatar
|
||||
|
||||
public TeamManager() {
|
||||
this.mpTeam = new TeamInfo();
|
||||
@@ -270,6 +284,18 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates all properties of the active team. */
|
||||
public void updateTeamProperties() {
|
||||
this.updateTeamResonances(); // Update team resonances.
|
||||
this.getPlayer()
|
||||
.sendPacket(new PacketSceneTeamUpdateNotify(this.getPlayer())); // Notify the player.
|
||||
|
||||
// Skill charges packet - Yes, this is official server behavior as of 2.6.0
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.forEach(Avatar::sendSkillExtraChargeMap);
|
||||
}
|
||||
|
||||
public void updateTeamEntities(BasePacket responsePacket) {
|
||||
// Sanity check - Should never happen
|
||||
if (this.getCurrentTeamInfo().getAvatars().size() <= 0) {
|
||||
@@ -277,9 +303,9 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
|
||||
// If current team has changed
|
||||
EntityAvatar currentEntity = this.getCurrentAvatarEntity();
|
||||
Int2ObjectMap<EntityAvatar> existingAvatars = new Int2ObjectOpenHashMap<>();
|
||||
int prevSelectedAvatarIndex = -1;
|
||||
var currentEntity = this.getCurrentAvatarEntity();
|
||||
var existingAvatars = new Int2ObjectOpenHashMap<EntityAvatar>();
|
||||
var prevSelectedAvatarIndex = -1;
|
||||
|
||||
for (EntityAvatar entity : this.getActiveTeam()) {
|
||||
existingAvatars.put(entity.getAvatar().getAvatarId(), entity);
|
||||
@@ -290,9 +316,8 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
|
||||
// Add back entities into team
|
||||
for (int i = 0; i < this.getCurrentTeamInfo().getAvatars().size(); i++) {
|
||||
int avatarId = this.getCurrentTeamInfo().getAvatars().get(i);
|
||||
var avatarId = (int) this.getCurrentTeamInfo().getAvatars().get(i);
|
||||
EntityAvatar entity;
|
||||
|
||||
if (existingAvatars.containsKey(avatarId)) {
|
||||
entity = existingAvatars.get(avatarId);
|
||||
existingAvatars.remove(avatarId);
|
||||
@@ -309,7 +334,7 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
|
||||
// Unload removed entities
|
||||
for (EntityAvatar entity : existingAvatars.values()) {
|
||||
for (var entity : existingAvatars.values()) {
|
||||
this.getPlayer().getScene().removeEntity(entity);
|
||||
entity.getAvatar().save();
|
||||
}
|
||||
@@ -323,18 +348,11 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
}
|
||||
this.currentCharacterIndex = prevSelectedAvatarIndex;
|
||||
|
||||
// Update team resonances
|
||||
this.updateTeamResonances();
|
||||
// Update properties.
|
||||
// Notify player.
|
||||
this.updateTeamProperties();
|
||||
|
||||
// Packets
|
||||
this.getPlayer().getWorld().broadcastPacket(new PacketSceneTeamUpdateNotify(this.getPlayer()));
|
||||
|
||||
// Skill charges packet - Yes, this is official server behavior as of 2.6.0
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.forEach(Avatar::sendSkillExtraChargeMap);
|
||||
|
||||
// Run callback
|
||||
// Send response packet.
|
||||
if (responsePacket != null) {
|
||||
this.getPlayer().sendPacket(responsePacket);
|
||||
}
|
||||
@@ -402,6 +420,138 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
this.addAvatarsToTeam(teamInfo, newTeam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup avatars for a trial avatar team.
|
||||
*
|
||||
* @param save Should the original team be saved?
|
||||
*/
|
||||
public void setupTrialAvatars(boolean save) {
|
||||
this.setPreviousIndex(this.getCurrentCharacterIndex());
|
||||
|
||||
if (save) {
|
||||
var originalTeam = getCurrentTeamInfo();
|
||||
this.getTrialAvatarTeam().copyFrom(originalTeam);
|
||||
} else this.getActiveTeam().clear();
|
||||
|
||||
this.usingTrialTeam = true;
|
||||
}
|
||||
|
||||
/** Displays the trial avatars. Picks the last avatar in the team. */
|
||||
public void trialAvatarTeamPostUpdate() {
|
||||
this.trialAvatarTeamPostUpdate(this.getActiveTeam().size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the trial avatars.
|
||||
*
|
||||
* @param newCharacterIndex The avatar to equip.
|
||||
*/
|
||||
public void trialAvatarTeamPostUpdate(int newCharacterIndex) {
|
||||
this.setCurrentCharacterIndex(Math.min(newCharacterIndex, this.getActiveTeam().size() - 1));
|
||||
|
||||
this.updateTeamProperties();
|
||||
this.getPlayer().getScene().addEntity(this.getCurrentAvatarEntity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an avatar to the trial team.
|
||||
*
|
||||
* @param trialAvatar The avatar to add.
|
||||
*/
|
||||
public void addAvatarToTrialTeam(Avatar trialAvatar) {
|
||||
// Remove the existing team's avatars.
|
||||
this.getActiveTeam()
|
||||
.forEach(
|
||||
x ->
|
||||
this.getPlayer()
|
||||
.getScene()
|
||||
.removeEntity(x, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE));
|
||||
// Remove the existing avatar from the teams if it exists.
|
||||
this.getActiveTeam().removeIf(x -> x.getAvatar().getAvatarId() == trialAvatar.getAvatarId());
|
||||
this.getCurrentTeamInfo().getAvatars().removeIf(x -> x == trialAvatar.getAvatarId());
|
||||
// Add the avatar to the teams.
|
||||
this.getActiveTeam().add(new EntityAvatar(this.getPlayer().getScene(), trialAvatar));
|
||||
this.getCurrentTeamInfo().addAvatar(trialAvatar);
|
||||
this.getTrialAvatars().put(trialAvatar.getAvatarId(), trialAvatar);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GUID of a trial avatar.
|
||||
*
|
||||
* @param avatarId The avatar ID.
|
||||
* @return The GUID of the avatar.
|
||||
*/
|
||||
public long getTrialAvatarGuid(int avatarId) {
|
||||
return getTrialAvatars().values().stream()
|
||||
.filter(avatar -> avatar.getTrialAvatarId() == avatarId)
|
||||
.map(avatar -> avatar.getGuid())
|
||||
.findFirst()
|
||||
.orElse(0L);
|
||||
}
|
||||
|
||||
/** Rollback changes from using a trial avatar team. */
|
||||
public void unsetTrialAvatarTeam() {
|
||||
this.trialAvatarTeamPostUpdate(this.getPreviousIndex());
|
||||
this.setPreviousIndex(-1);
|
||||
}
|
||||
|
||||
/** Removes all avatars from the trial avatar team. */
|
||||
public void removeTrialAvatarTeam() {
|
||||
this.removeTrialAvatarTeam(
|
||||
this.getActiveTeam().stream().map(avatar -> avatar.getAvatar().getAvatarId()).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one avatar from the trial avatar team.
|
||||
*
|
||||
* @param avatarId The avatar ID to remove.
|
||||
*/
|
||||
public void removeTrialAvatarTeam(int avatarId) {
|
||||
this.removeTrialAvatarTeam(List.of(avatarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a collection of avatars from the trial avatar team.
|
||||
*
|
||||
* @param avatarIds The avatar IDs to remove.
|
||||
*/
|
||||
public void removeTrialAvatarTeam(List<Integer> avatarIds) {
|
||||
var player = this.getPlayer();
|
||||
|
||||
// Disable the trial team.
|
||||
this.usingTrialTeam = false;
|
||||
this.trialAvatarTeam = new TeamInfo();
|
||||
|
||||
// Remove the avatars from the team.
|
||||
avatarIds.forEach(
|
||||
avatarId -> {
|
||||
this.getActiveTeam()
|
||||
.forEach(
|
||||
x ->
|
||||
player
|
||||
.getScene()
|
||||
.removeEntity(x, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE));
|
||||
this.getActiveTeam().removeIf(x -> x.getAvatar().getTrialAvatarId() == avatarId);
|
||||
this.getTrialAvatars().values().removeIf(x -> x.getTrialAvatarId() == avatarId);
|
||||
});
|
||||
|
||||
// Re-add the avatars to the team.
|
||||
var index = 0;
|
||||
for (var avatar : this.getCurrentTeamInfo().getAvatars()) {
|
||||
if (this.getActiveTeam().stream()
|
||||
.map(entity -> entity.getAvatar().getAvatarId())
|
||||
.toList()
|
||||
.contains(avatar)) return;
|
||||
|
||||
this.getActiveTeam()
|
||||
.add(
|
||||
index++,
|
||||
new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatar)));
|
||||
}
|
||||
|
||||
this.unsetTrialAvatarTeam();
|
||||
}
|
||||
|
||||
public void setupTemporaryTeam(List<List<Long>> guidList) {
|
||||
this.temporaryTeam =
|
||||
guidList.stream()
|
||||
@@ -725,4 +875,195 @@ public class TeamManager extends BasePlayerDataManager {
|
||||
player.sendPacket(new PacketAvatarTeamAllDataNotify(player));
|
||||
player.sendPacket(new PacketDelBackupAvatarTeamRsp(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies abilities for the currently selected team. These abilities are sourced from the scene.
|
||||
*
|
||||
* @param scene The scene with the abilities to apply.
|
||||
*/
|
||||
public void applyAbilities(Scene scene) {
|
||||
try {
|
||||
var levelEntityConfig = scene.getSceneData().getLevelEntityConfig();
|
||||
var config = GameData.getConfigLevelEntityDataMap().get(levelEntityConfig);
|
||||
if (config == null) return;
|
||||
|
||||
var avatars = this.getPlayer().getAvatars();
|
||||
var avatarIds = scene.getSceneData().getSpecifiedAvatarList();
|
||||
var specifiedAvatarList = this.getActiveTeam();
|
||||
|
||||
if (avatarIds != null && avatarIds.size() > 0) {
|
||||
// certain scene could limit specific avatars' entry
|
||||
specifiedAvatarList.clear();
|
||||
for (int id : avatarIds) {
|
||||
var avatar = avatars.getAvatarById(id);
|
||||
if (avatar == null) continue;
|
||||
|
||||
specifiedAvatarList.add(new EntityAvatar(scene, avatar));
|
||||
}
|
||||
}
|
||||
|
||||
for (var entityAvatar : specifiedAvatarList) {
|
||||
var avatarData = entityAvatar.getAvatar().getAvatarData();
|
||||
if (avatarData == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
avatarData.buildEmbryo(); // Create avatar abilities.
|
||||
if (config.getAvatarAbilities() == null) {
|
||||
continue; // continue and not break because has to rebuild ability for the next avatar if
|
||||
// any
|
||||
}
|
||||
|
||||
for (ConfigAbilityData abilities : config.getAvatarAbilities()) {
|
||||
avatarData.getAbilities().add(Utils.abilityHash(abilities.getAbilityName()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger()
|
||||
.error(
|
||||
"Error applying level entity config for scene {}", scene.getSceneData().getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Integer> getTrialAvatarParam(int trialAvatarId) {
|
||||
if (GameData.getTrialAvatarCustomData()
|
||||
.isEmpty()) { // use default data if custom data not available
|
||||
if (GameData.getTrialAvatarDataMap().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
return GameData.getTrialAvatarDataMap().get(trialAvatarId).getTrialAvatarParamList();
|
||||
}
|
||||
// use custom data
|
||||
if (GameData.getTrialAvatarCustomData().get(trialAvatarId) == null) return List.of();
|
||||
|
||||
val trialCustomParams =
|
||||
GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
|
||||
return trialCustomParams.isEmpty()
|
||||
? List.of()
|
||||
: Stream.of(trialCustomParams.get(0).split(";")).map(Integer::parseInt).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trial avatar to the player's team.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
* @param questMainId The quest ID associated with the quest.
|
||||
* @param reason The reason for granting the avatar.
|
||||
* @return True if the avatar was added, false otherwise.
|
||||
*/
|
||||
public boolean addTrialAvatar(int avatarId, int questMainId, GrantReason reason) {
|
||||
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(avatarId);
|
||||
if (trialAvatarBasicParam.isEmpty()) return false;
|
||||
|
||||
var avatar = new Avatar(trialAvatarBasicParam.get(0));
|
||||
if (avatar.getAvatarData() == null || !this.getPlayer().hasSentLoginPackets()) return false;
|
||||
|
||||
avatar.setOwner(this.getPlayer());
|
||||
// Add trial weapons and relics.
|
||||
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), avatarId, reason, questMainId);
|
||||
avatar.equipTrialItems();
|
||||
// Re-calculate stats
|
||||
avatar.recalcStats();
|
||||
|
||||
// Packet, mimic official server behaviour, add to player's bag but not saving to database.
|
||||
this.getPlayer().sendPacket(new PacketAvatarAddNotify(avatar, false));
|
||||
// Add to avatar to the temporary trial team.
|
||||
this.addAvatarToTrialTeam(avatar);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trial avatar to the player's team.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
* @param questMainId The quest ID associated with the quest.
|
||||
*/
|
||||
public void addTrialAvatar(int avatarId, int questMainId) {
|
||||
this.addTrialAvatars(List.of(avatarId), questMainId, true);
|
||||
|
||||
// Packet, mimic official server behaviour, necessary to stop player from modifying team.
|
||||
this.getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(this.getPlayer()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds) {
|
||||
this.addTrialAvatars(avatarIds, 0, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
* @param save Whether to retain the currently equipped avatars.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds, boolean save) {
|
||||
this.addTrialAvatars(avatarIds, 0, save);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of trial avatars to the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
* @param questId The ID of the quest this trial team is associated with.
|
||||
* @param save Whether to retain the currently equipped avatars.
|
||||
*/
|
||||
public void addTrialAvatars(List<Integer> avatarIds, int questId, boolean save) {
|
||||
this.setupTrialAvatars(save); // Perform initial setup.
|
||||
|
||||
// Add the avatars to the team.
|
||||
avatarIds.forEach(
|
||||
avatarId -> {
|
||||
var result =
|
||||
this.addTrialAvatar(
|
||||
avatarId,
|
||||
questId,
|
||||
questId == 0
|
||||
? GrantReason.GRANT_REASON_BY_QUEST
|
||||
: GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY);
|
||||
|
||||
if (!result) throw new RuntimeException("Unable to add trial avatar to team.");
|
||||
});
|
||||
|
||||
// Update the team.
|
||||
this.trialAvatarTeamPostUpdate(questId == 0 ? getActiveTeam().size() - 1 : 0);
|
||||
}
|
||||
|
||||
/** Removes all trial avatars from the player's team. */
|
||||
public void removeTrialAvatar() {
|
||||
this.removeTrialAvatar(
|
||||
this.getActiveTeam().stream()
|
||||
.map(EntityAvatar::getAvatar)
|
||||
.map(Avatar::getAvatarId)
|
||||
.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a trial avatar from the player's team. Additionally, unlocks the ability to change the
|
||||
* team configuration.
|
||||
*
|
||||
* @param avatarId The ID of the avatar.
|
||||
*/
|
||||
public void removeTrialAvatar(int avatarId) {
|
||||
this.removeTrialAvatar(List.of(avatarId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a collection of trial avatars from the player's team.
|
||||
*
|
||||
* @param avatarIds List of trial avatar IDs.
|
||||
*/
|
||||
public void removeTrialAvatar(List<Integer> avatarIds) {
|
||||
if (!this.isUsingTrialTeam()) throw new RuntimeException("Player is not using a trial team.");
|
||||
|
||||
this.getPlayer()
|
||||
.sendPacket(
|
||||
new PacketAvatarDelNotify(avatarIds.stream().map(this::getTrialAvatarGuid).toList()));
|
||||
this.removeTrialAvatarTeam(avatarIds);
|
||||
|
||||
// Update the team.
|
||||
if (avatarIds.size() == 1) this.getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user