Updated TrialAvatar activity all functionality

This commit is contained in:
WQJ 2023-01-05 17:52:46 +11:00
parent 8460ec9111
commit e50f5f3595
20 changed files with 512 additions and 148 deletions

View File

@ -644,7 +644,6 @@ public class ResourceLoader {
getResourcePath(pathName + "TrialAvatarData.json"),
TrialAvatarCustomData.class).forEach(instance -> {
instance.onLoad();
Grasscutter.getLogger().info("{}", instance);
GameData.getTrialAvatarCustomData()
.put(instance.getTrialAvatarId(), instance);
});

View File

@ -1,5 +1,13 @@
package emu.grasscutter.game.activity.trialavatar;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.custom.TrialAvatarActivityDataCustomData;
import emu.grasscutter.data.excels.RewardData;
import emu.grasscutter.data.excels.TrialAvatarActivityDataData;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.activity.ActivityHandler;
import emu.grasscutter.game.activity.GameActivity;
import emu.grasscutter.game.activity.PlayerActivityData;
@ -7,10 +15,13 @@ import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.net.proto.ActivityInfoOuterClass.ActivityInfo;
import emu.grasscutter.utils.JsonUtils;
import java.util.stream.Collectors;
import java.util.*;
import java.util.stream.*;
import lombok.*;
@GameActivity(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR)
public class TrialAvatarActivityHandler extends ActivityHandler {
@Getter @Setter private int selectedTrialAvatarIndex;
@Override
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
@ -34,4 +45,68 @@ public class TrialAvatarActivityHandler extends ActivityHandler {
return JsonUtils.decode(playerActivityData.getDetail(), TrialAvatarPlayerData.class);
}
public int getTrialActivityDungeonId(int trialAvatarIndexId) {
if (GameData.getTrialAvatarActivityDataCustomData().isEmpty()) {
if (GameData.getTrialAvatarActivityDataDataMap().get(trialAvatarIndexId) == null) return 0;
return GameData.getTrialAvatarActivityDataDataMap().get(trialAvatarIndexId).getDungeonId();
}
if (GameData.getTrialAvatarActivityDataCustomData().get(trialAvatarIndexId) == null) return 0;
return GameData.getTrialAvatarActivityDataCustomData().get(trialAvatarIndexId).getDungeonId();
}
public boolean enterTrialDungeon(Player player, int trialAvatarIndexId, int enterPointId) {
if (!player.getServer().getDungeonSystem().enterDungeon(
player,
enterPointId,
getTrialActivityDungeonId(trialAvatarIndexId))) return false;
setSelectedTrialAvatarIndex(trialAvatarIndexId);
return true;
}
public List<Integer> getBattleAvatarsList() {
if (GameData.getTrialAvatarActivityDataCustomData().isEmpty()) {
TrialAvatarActivityDataData activityData = GameData.getTrialAvatarActivityDataDataMap()
.get(getSelectedTrialAvatarIndex());
if (activityData == null && activityData.getBattleAvatarsList().isBlank()) return List.of();
return Stream.of(activityData.getBattleAvatarsList().split(",")).map(Integer::parseInt).toList();
}
TrialAvatarActivityDataCustomData activityCustomData = GameData.getTrialAvatarActivityDataCustomData()
.get(getSelectedTrialAvatarIndex());
if (activityCustomData == null && activityCustomData.getBattleAvatarsList().isBlank()) return List.of();
return Stream.of(activityCustomData.getBattleAvatarsList().split(",")).map(Integer::parseInt).toList();
}
public void setupTrialAvatarTeam(Player player) {
if (getSelectedTrialAvatarIndex() <= 0) return;
List<Integer> battleAvatarsList = getBattleAvatarsList();
if (battleAvatarsList.isEmpty()) return;
player.addTrialAvatarsForActivity(battleAvatarsList);
emu.grasscutter.Grasscutter.getLogger().info("{}", player.getTeamManager().getActiveTeam().stream().map(x -> x.getAvatar().getAvatarId()).toList());
}
public void unsetTrialAvatarTeam(Player player) {
if (getSelectedTrialAvatarIndex() <= 0) return;
player.removeTrialAvatarForActivity();
setSelectedTrialAvatarIndex(0);
}
public boolean getReward(Player player, PlayerActivityData playerActivityData, int trialAvatarIndexId) {
TrialAvatarPlayerData trialAvatarPlayerData = getTrialAvatarPlayerData(playerActivityData);
TrialAvatarPlayerData.RewardInfoItem rewardInfo = trialAvatarPlayerData.getRewardInfo(trialAvatarIndexId);
if (rewardInfo == null) return false;
RewardData rewardParam = GameData.getRewardDataMap().get(rewardInfo.getRewardId());
if (rewardParam == null) return false;
player.getInventory().addItemParamDatas(rewardParam.getRewardItemList(), ActionReason.TrialAvatarActivityFirstPassReward);
rewardInfo.setReceivedReward(true);
playerActivityData.setDetail(trialAvatarPlayerData);
playerActivityData.save();
return true;
}
}

View File

@ -19,16 +19,36 @@ import java.util.stream.*;
public class TrialAvatarPlayerData {
List<RewardInfoItem> rewardInfoList;
private static List<Integer> getAvatarIdList(int scheduleId) {
if (GameData.getTrialAvatarActivityCustomData().isEmpty()) {
if (GameData.getTrialAvatarActivityDataMap().get(scheduleId) == null) return List.of();
return GameData.getTrialAvatarActivityDataMap().get(scheduleId).getAvatarIndexIdList();
}
if (GameData.getTrialAvatarActivityCustomData().get(scheduleId) == null) return List.of();
return GameData.getTrialAvatarActivityCustomData().get(scheduleId).getAvatarIndexIdList();
}
private static List<Integer> getRewardIdList(int scheduleId) {
if (GameData.getTrialAvatarActivityCustomData().isEmpty()) {
if (GameData.getTrialAvatarActivityDataMap().get(scheduleId) == null) return List.of();
return GameData.getTrialAvatarActivityDataMap().get(scheduleId).getRewardIdList();
}
if (GameData.getTrialAvatarActivityCustomData().get(scheduleId) == null) return List.of();
return GameData.getTrialAvatarActivityCustomData().get(scheduleId).getRewardIdList();
}
public static TrialAvatarPlayerData create(int scheduleId) {
TrialAvatarActivityData activityData = GameData.getTrialAvatarActivityDataMap().get(scheduleId);
List<Integer> avatarIds = getAvatarIdList(scheduleId);
List<Integer> rewardIds = getRewardIdList(scheduleId);
return TrialAvatarPlayerData.of()
.rewardInfoList(activityData == null ? List.of() :
IntStream.range(0, activityData.getAvatarIndexIdList().size())
.filter(i -> activityData.getAvatarIndexIdList().get(i) > 0 && activityData.getRewardIdList().get(i) > 0)
.mapToObj(i -> RewardInfoItem.create(
activityData.getAvatarIndexIdList().get(i),
activityData.getRewardIdList().get(i)))
.collect(Collectors.toList()))
.rewardInfoList(IntStream.range(0, avatarIds.size())
.filter(i -> avatarIds.get(i) > 0 && rewardIds.get(i) > 0)
.mapToObj(i -> RewardInfoItem.create(
avatarIds.get(i),
rewardIds.get(i)))
.collect(Collectors.toList()))
.build();
}
@ -39,6 +59,11 @@ public class TrialAvatarPlayerData {
.build();
}
public RewardInfoItem getRewardInfo(int trialAvatarIndexId) {
return getRewardInfoList().stream().filter(x -> x.getTrialAvatarIndexId() == trialAvatarIndexId)
.findFirst().orElse(null);
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
@Builder(builderMethodName = "of")
@ -52,7 +77,7 @@ public class TrialAvatarPlayerData {
return RewardInfoItem.of()
.trialAvatarIndexId(trialAvatarIndexId)
.rewardId(rewardId)
.passedDungeon(false)
.passedDungeon(true)
.receivedReward(false)
.build();
}

View File

@ -165,12 +165,6 @@ public class Avatar {
this.guid = player.getNextGameGuid();
}
public void removeOwner() {
this.owner = null;
this.ownerId = 0;
this.guid = 0;
}
static public int getMinPromoteLevel(int level) {
if (level > 80) {
return 6;
@ -958,58 +952,93 @@ public class Avatar {
return showAvatarInfo.build();
}
public void setTrialAvatarInfo(TrialAvatarData trialAvatar, GrantReason grantReason, int fromParentQuestId){
this.setLevel(trialAvatar.getTrialAvatarParamList().get(1));
this.setTrialItems(trialAvatar.getTrialAvatarParamList().get(1));
this.setTrialAvatarId(trialAvatar.getTrialAvatarId());
public void setTrialAvatarInfo(int avatarLevel, int trialAvatarId, GrantReason grantReason, int fromParentQuestId){
this.setLevel(avatarLevel);
this.setPromoteLevel(getMinPromoteLevel(avatarLevel));
this.setTrialAvatarId(trialAvatarId);
this.setGrantReason(grantReason.getNumber());
this.setFromParentQuestId(fromParentQuestId);
this.setAvatarType(2);
this.setTrialSkillLevel();
this.setTrialItems();
}
public void setTrialItems(int trialAvatarLevel){
// add enhanced verion of trial weapon
GameItem weapon = new GameItem(getAvatarData().getInitialWeapon()+100);
if (weapon.getItemData() == null){
weapon = new GameItem(getAvatarData().getInitialWeapon());
}
weapon.setLevel(trialAvatarLevel);
weapon.setExp(0);
weapon.setPromoteLevel(0);
getEquips().put(weapon.getEquipSlot(), weapon);
public int getTrialSkillLevel() {
if (GameData.getTrialAvatarCustomData().isEmpty()) { // use default data if custom data not available
int trialAvatarTemplateLevel = getLevel() <= 9 ? 1 :
(int) (Math.floor(getLevel() / 10) * 10); // round trial level to fit template levels
int trialAvatarTemplateLevel = trialAvatarLevel <= 9 ? 1 :
(int) (Math.floor(trialAvatarLevel / 10) * 10); // round trial level to fit template levels
TrialAvatarTemplateData templateData = GameData.getTrialAvatarTemplateDataMap().get(trialAvatarTemplateLevel);
return templateData == null ? 1 : templateData.getTrialAvatarSkillLevel();
}
if (GameData.getTrialAvatarCustomData().get(getTrialAvatarId()) == null) return 1;
return GameData.getTrialAvatarCustomData().get(getTrialAvatarId()).getCoreProudSkillLevel(); // enhanced version of weapon
}
public void setTrialSkillLevel() {
getSkillLevelMap().keySet().stream().forEach(skill -> setSkillLevel(skill, getTrialSkillLevel()));
}
public int getTrialWeaponId() {
if (GameData.getTrialAvatarCustomData().isEmpty()) { // use default data if custom data not available
if (GameData.getTrialAvatarDataMap().get(getTrialAvatarId()) == null)
return getAvatarData().getInitialWeapon();
return GameData.getItemDataMap().get(getAvatarData().getInitialWeapon()+100) == null ?
getAvatarData().getInitialWeapon() :
getAvatarData().getInitialWeapon()+100; // enhanced version of weapon
}
// use custom data
if (GameData.getTrialAvatarCustomData().get(getTrialAvatarId()) == null) return 0;
val trialCustomParams = GameData.getTrialAvatarCustomData().get(trialAvatarId).getTrialAvatarParamList();
return trialCustomParams.size() < 2 ? getAvatarData().getInitialWeapon() :
Integer.parseInt(trialCustomParams.get(1).split(";")[0]);
}
public List<Integer> getTrialReliquary() {
if (!GameData.getTrialAvatarCustomData().isEmpty()) {
// try using custom data
if (GameData.getTrialAvatarCustomData().get(getTrialAvatarId()) != null) {
val trialCustomParams = GameData.getTrialAvatarCustomData().get(getTrialAvatarId()).getTrialAvatarParamList();
if (trialCustomParams.size() > 2) {
return Stream.of(trialCustomParams.get(2).split(";")).map(Integer::parseInt).toList();
}
}
}
int trialAvatarTemplateLevel = getLevel() <= 9 ? 1 :
(int) (Math.floor(getLevel() / 10) * 10); // round trial level to fit template levels
TrialAvatarTemplateData templateData = GameData.getTrialAvatarTemplateDataMap().get(trialAvatarTemplateLevel);
if (templateData == null){
return;
}
return templateData == null ? List.of() : templateData.getTrialReliquaryList();
}
// add trial artifacts
for (int id : templateData.getTrialReliquaryList()){
public void setTrialItems(){
// add enhanced verion of trial weapon
GameItem weapon = new GameItem(getTrialWeaponId());
weapon.setLevel(getLevel());
weapon.setExp(0);
weapon.setPromoteLevel(getMinPromoteLevel(getLevel()));
getEquips().put(weapon.getEquipSlot(), weapon);
// add Trial Artifacts
getTrialReliquary().forEach(id -> {
TrialReliquaryData reliquaryData = GameData.getTrialReliquaryDataMap().get(id);
if (reliquaryData == null) continue;
if (reliquaryData == null) return;
GameItem relic = new GameItem(reliquaryData.getReliquaryId());
relic.setLevel(reliquaryData.getLevel());
relic.setMainPropId(reliquaryData.getMainPropId());
List<Integer> appendPropList = reliquaryData.getAppendPropList();
if (appendPropList.size() > 0){
for (Integer trialAppendProp : appendPropList){
relic.getAppendPropIdList().add(trialAppendProp);
}
}
relic.getAppendPropIdList().addAll(reliquaryData.getAppendPropList());
getEquips().put(relic.getEquipSlot(), relic);
}
});
// add costume if any (ambor, rosiaria, mona, Jean)
for (AvatarCostumeData costumeData : GameData.getAvatarCostumeDataItemIdMap().values()){
if (costumeData.getCharacterId() == this.getAvatarId()){
this.setCostume(costumeData.getId());
break;
}
}
GameData.getAvatarCostumeDataItemIdMap().values().stream().forEach(costumeData -> {
if (costumeData.getCharacterId() != getAvatarId()) return;
setCostume(costumeData.getId());
});
}
public void equipTrialItems(){
@ -1023,14 +1052,6 @@ public class Avatar {
});
}
public void removeTrialItems(){
getEquips().forEach((itemEquipTypeValues, item) -> {
item.setEquipCharacter(0);
item.removeOwner();
});
getEquips().clear();
}
public TrialAvatarInfo trialAvatarInfoProto(){
TrialAvatarInfo.Builder trialAvatar = TrialAvatarInfo.newBuilder()
.setTrialAvatarId(this.getTrialAvatarId())

View File

@ -98,12 +98,11 @@ public class DungeonSystem extends BaseGameSystem {
if (player.getWorld().transferPlayerToScene(player, sceneId, data)) {
scene = player.getScene();
var dungeonManager = new DungeonManager(scene, data);
dungeonManager.startDungeon();
scene.addDungeonSettleObserver(basicDungeonSettleObserver);
}
scene.setPrevScenePoint(pointId);
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId));
player.sendPacket(new PacketPlayerEnterDungeonRsp(pointId, dungeonId)); // todo, dont have to send packet for trial avatar activity dungeon
return true;
}
@ -154,7 +153,10 @@ public class DungeonSystem extends BaseGameSystem {
player.getTowerManager().clearEntry();
// Transfer player back to world
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
Grasscutter.getGameServer().getScheduler().scheduleDelayedTask(() -> {
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
}, dungeonManager.isFinishedSuccessfully() ? 0 : 3);
player.sendPacket(new BasePacket(PacketOpcodes.PlayerQuitDungeonRsp));
}

View File

@ -133,11 +133,6 @@ public class GameItem {
}
}
public void removeOwner() {
this.ownerId = 0;
this.guid = 0;
}
public ObjectId getObjectId() {
return id;
}

View File

@ -79,12 +79,14 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.LinkedBlockingQueue;
@ -808,21 +810,30 @@ public class Player {
addAvatar(new Avatar(avatarId), true);
}
public boolean addTrialAvatarForQuest(int trialAvatarId, GrantReason reason, int questMainId){
// TODO, other trial avatar like activity and element trial dungeon might have
// completely different scenario, this function is currently used for Quest Exec only
TrialAvatarData trialAvatar = GameData.getTrialAvatarDataMap().get(trialAvatarId);
if (trialAvatar == null) return false;
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();
List<Integer> trialParams = trialAvatar.getTrialAvatarParamList();
if (trialParams == null || trialParams.size() < 2) return false;
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();
}
Avatar avatar = new Avatar(trialParams.get(0));
public boolean addTrialAvatar(int trialAvatarId, GrantReason reason, int questMainId){
List<Integer> trialAvatarBasicParam = getTrialAvatarParam(trialAvatarId);
if (trialAvatarBasicParam.isEmpty()) return false;
Avatar avatar = new Avatar(trialAvatarBasicParam.get(0));
if (avatar.getAvatarData() == null || !hasSentLoginPackets()) return false;
avatar.setOwner(this);
// Add trial weapons and relics
avatar.setTrialAvatarInfo(trialAvatar, reason, questMainId);
avatar.setTrialAvatarInfo(trialAvatarBasicParam.get(1), trialAvatarId, reason, questMainId);
avatar.equipTrialItems();
// Recalc stats
avatar.recalcStats();
@ -831,28 +842,47 @@ public class Player {
sendPacket(new PacketAvatarAddNotify(avatar, false));
// add to avatar to temporary trial team
getTeamManager().addAvatarToTrialTeam(avatar);
return true;
}
public boolean addTrialAvatarForQuest(int trialAvatarId, int questMainId) {
getTeamManager().setupTrialAvatarTeamForQuest();
if (!addTrialAvatar(
trialAvatarId,
GrantReason.GRANT_REASON_BY_QUEST,
questMainId)) return false;
getTeamManager().trialAvatarTeamPostUpdate();
// Packet, mimic official server behaviour, neccessary to stop player from modifying team
sendPacket(new PacketAvatarTeamUpdateNotify(this));
return true;
}
public boolean removeTrialAvatar(int trialAvatarId){
EntityAvatar trialEntityAvatar = getTeamManager().trialAvatarInTeam(trialAvatarId);
if (trialEntityAvatar == null) return false;
public void addTrialAvatarsForActivity(List<Integer> trialAvatarIds) {
getTeamManager().setupTrialAvatarTeamForActivity();
trialAvatarIds.forEach(trialAvatarId -> addTrialAvatar(
trialAvatarId,
GrantReason.GRANT_REASON_BY_TRIAL_AVATAR_ACTIVITY,
0));
getTeamManager().trialAvatarTeamPostUpdate(0);
}
Avatar trialAvatar = trialEntityAvatar.getAvatar();
// Packet, mimic official server behaviour
sendPacket(new PacketAvatarDelNotify(Arrays.asList(trialAvatar.getGuid())));
// Reset temporary trial team
getTeamManager().removeAvatarFromTrialTeam(trialEntityAvatar);
public boolean removeTrialAvatarForQuest(int trialAvatarId) {
if (!getTeamManager().isUseTrialTeam()) return false;
trialAvatar.removeTrialItems();
trialAvatar.removeOwner();
// Packet, mimic official server behaviour, necessary to unlock team modifying
sendPacket(new PacketAvatarDelNotify(List.of(getTeamManager().getTrialAvatarGuid(trialAvatarId))));
getTeamManager().removeTrialAvatarTeamForQuest(trialAvatarId);
sendPacket(new PacketAvatarTeamUpdateNotify());
return true;
}
public void removeTrialAvatarForActivity() {
if (!getTeamManager().isUseTrialTeam()) return;
sendPacket(new PacketAvatarDelNotify(getTeamManager().getActiveTeam().stream()
.map(x -> x.getAvatar().getGuid()).toList()));
getTeamManager().removeTrialAvatarTeamForActivity();
}
public void addFlycloak(int flycloakId) {
this.getFlyCloakList().add(flycloakId);
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));

View File

@ -21,6 +21,7 @@ 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.VisionTypeOuterClass.VisionType;
import emu.grasscutter.server.event.player.PlayerTeamDeathEvent;
import emu.grasscutter.server.packet.send.PacketAddCustomTeamRsp;
import emu.grasscutter.server.packet.send.PacketAvatarDieAnimationEndRsp;
@ -63,8 +64,10 @@ public class TeamManager extends BasePlayerDataManager {
@Transient private int useTemporarilyTeamIndex = -1;
@Transient private List<TeamInfo> temporaryTeam; // Temporary Team for tower
// Trial Teams, using hashmap not list since only one unique trial avatar can be in a single team
@Transient @Getter private Map<Integer, Long> trialTeamGuid;
@Transient @Getter @Setter private boolean useTrialTeam;
@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() {
@ -73,7 +76,8 @@ public class TeamManager extends BasePlayerDataManager {
this.gadgets = new HashSet<>();
this.teamResonances = new IntOpenHashSet();
this.teamResonancesConfig = new IntOpenHashSet();
this.trialTeamGuid = new HashMap<>();
this.trialAvatars = new HashMap<>();
this.trialAvatarTeam = new TeamInfo();
}
public TeamManager(Player player) {
@ -119,6 +123,7 @@ public class TeamManager extends BasePlayerDataManager {
}
public TeamInfo getCurrentTeamInfo() {
if (useTrialTeam) return this.getTrialAvatarTeam();
if (useTemporarilyTeamIndex >= 0 &&
useTemporarilyTeamIndex < temporaryTeam.size()) {
return temporaryTeam.get(useTemporarilyTeamIndex);
@ -418,54 +423,75 @@ public class TeamManager extends BasePlayerDataManager {
this.addAvatarsToTeam(teamInfo, newTeam);
}
public void addAvatarToTrialTeam(Avatar avatar){
// add to trial team
getTrialTeamGuid().put(avatar.getAvatarId(), avatar.getGuid());
public void setupTrialAvatarTeamForQuest() {
setPreviousIndex(getCurrentCharacterIndex());
EntityAvatar newEntity = new EntityAvatar(getPlayer().getScene(), avatar);
boolean inTeam = false;
int index;
// replace avatar with trial avatar if in team already
// Note: index increments to the size of active team before exiting loop
for (index = 0; index < getActiveTeam().size(); index++){
EntityAvatar activeEntity = getActiveTeam().get(index);
if (activeEntity.getAvatar().getAvatarId() == avatar.getAvatarId()){
inTeam = true;
getActiveTeam().set(index, newEntity);
break;
}
}
if (!inTeam) getActiveTeam().add(newEntity);
// select the newly added trial avatar
// Limit character index in case it's out of bounds
setCurrentCharacterIndex(index >= getActiveTeam().size() ? index-1 : index);
updateTeamProperties(); // is necessary to update team at scene
TeamInfo originalTeam = getCurrentTeamInfo();
this.useTrialTeam = true;
getTrialAvatarTeam().copyFrom(originalTeam);
}
public EntityAvatar trialAvatarInTeam(int trialAvatarId) {
return getActiveTeam().stream()
.filter(entityAvatar -> entityAvatar.getAvatar().getTrialAvatarId() == trialAvatarId)
.findFirst()
.orElse(null);
public void setupTrialAvatarTeamForActivity() {
setPreviousIndex(getCurrentCharacterIndex());
getActiveTeam().clear();
this.useTrialTeam = true;
}
public void removeAvatarFromTrialTeam(EntityAvatar trialAvatar) {
getTrialTeamGuid().remove(trialAvatar.getAvatar().getAvatarId());
getActiveTeam().remove(trialAvatar);
public void trialAvatarTeamPostUpdate() {
trialAvatarTeamPostUpdate(getActiveTeam().size()-1);
}
if (getPreviousIndex() > -1) { // restore character selection before adding trial avatar
setCurrentCharacterIndex(getPreviousIndex());
setPreviousIndex(-1);
}
public void trialAvatarTeamPostUpdate(int newCharacterIndex) {
setCurrentCharacterIndex(Math.min(newCharacterIndex, getActiveTeam().size() - 1));
updateTeamProperties();
getPlayer().getScene().addEntity(getCurrentAvatarEntity());
}
// Limit character index in case its out of bounds
if (getCurrentCharacterIndex() >= getActiveTeam().size() || getCurrentCharacterIndex() < 0) {
setCurrentCharacterIndex(getCurrentCharacterIndex() - 1);
}
public void addAvatarToTrialTeam(Avatar trialAvatar) {
getActiveTeam().forEach(x -> getPlayer().getScene().removeEntity(x, VisionType.VISION_TYPE_REMOVE));
getActiveTeam().removeIf(x -> x.getAvatar().getAvatarId() == trialAvatar.getAvatarId());
getCurrentTeamInfo().getAvatars().removeIf(x -> x == trialAvatar.getAvatarId());
getActiveTeam().add(new EntityAvatar(getPlayer().getScene(), trialAvatar));
getCurrentTeamInfo().addAvatar(trialAvatar);
getTrialAvatars().put(trialAvatar.getAvatarId(), trialAvatar);
}
updateTeamProperties();
public long getTrialAvatarGuid(int trialAvatarId) {
return getTrialAvatars().values().stream()
.filter(avatar -> avatar.getTrialAvatarId() == trialAvatarId)
.map(avatar -> avatar.getGuid())
.findFirst().orElse(0L);
}
public void unsetTrialAvatarTeam() {
trialAvatarTeamPostUpdate(getPreviousIndex());
setPreviousIndex(-1);
}
public void removeTrialAvatarTeamForQuest(int trialAvatarId) {
this.useTrialTeam = false;
this.trialAvatarTeam = new TeamInfo();
getActiveTeam().forEach(x -> getPlayer().getScene().removeEntity(x, VisionType.VISION_TYPE_REMOVE));
getActiveTeam().removeIf(x -> x.getAvatar().getTrialAvatarId() == trialAvatarId);
getTrialAvatars().values().removeIf(x -> x.getTrialAvatarId() == trialAvatarId);
int[] indexCounter = new int[]{-1};
getCurrentTeamInfo().getAvatars().forEach(avatarId -> {
indexCounter[0] += 1;
if (getActiveTeam().stream().map(x -> x.getAvatar().getAvatarId()).toList().contains(avatarId)) return;
getActiveTeam().add(indexCounter[0], new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)));
});
unsetTrialAvatarTeam();
}
public void removeTrialAvatarTeamForActivity() {
this.useTrialTeam = false;
this.trialAvatarTeam = new TeamInfo();
getActiveTeam().forEach(x -> getPlayer().getScene().removeEntity(x, VisionType.VISION_TYPE_REMOVE));
getActiveTeam().clear();
getTrialAvatars().clear();
getCurrentTeamInfo().getAvatars().forEach(avatarId -> {
getActiveTeam().add(new EntityAvatar(getPlayer().getScene(), getPlayer().getAvatars().getAvatarById(avatarId)));
});
unsetTrialAvatarTeam();
}
public void setupTemporaryTeam(List<List<Long>> guidList) {

View File

@ -202,7 +202,12 @@ public class GameQuest {
getQuestData().getFailExec().forEach(e -> getOwner().getServer().getQuestSystem().triggerExec(this, e, e.getParam()));
if (getQuestData().getTrialAvatarList() != null) {
getQuestData().getTrialAvatarList().forEach(t -> getOwner().removeTrialAvatarForQuest(t));
}
Grasscutter.getLogger().debug("Quest {} is failed", subQuestId);
}
// Return true if it did the rewind

View File

@ -7,15 +7,12 @@ import emu.grasscutter.game.quest.GameQuest;
import emu.grasscutter.game.quest.QuestValueExec;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
import emu.grasscutter.net.proto.TrialAvatarGrantRecordOuterClass.TrialAvatarGrantRecord.GrantReason;
@QuestValueExec(QuestExec.QUEST_EXEC_GRANT_TRIAL_AVATAR)
public class ExecGrantTrialAvatar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
if (quest.getOwner().addTrialAvatarForQuest(Integer.parseInt(paramStr[0]),
GrantReason.GRANT_REASON_BY_QUEST,
quest.getMainQuestId())) {
if (quest.getOwner().addTrialAvatarForQuest(Integer.parseInt(paramStr[0]), quest.getMainQuestId())) {
Grasscutter.getLogger().info("Added trial avatar to team for quest {}", quest.getSubQuestId());
return true;
}

View File

@ -12,7 +12,7 @@ import emu.grasscutter.game.quest.handlers.QuestExecHandler;
public class ExecRemoveTrialAvatar extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
if (quest.getOwner().removeTrialAvatar(Integer.parseInt(paramStr[0]))) {
if (quest.getOwner().removeTrialAvatarForQuest(Integer.parseInt(paramStr[0]))) {
Grasscutter.getLogger().info("Removed trial avatar from team for quest {}", quest.getSubQuestId());
return true;
}

View File

@ -213,8 +213,14 @@ public class Scene {
// Add new entities for player
TeamInfo teamInfo = player.getTeamManager().getCurrentTeamInfo();
for (int avatarId : teamInfo.getAvatars()) {
EntityAvatar entity = new EntityAvatar(player.getScene(), player.getAvatars().getAvatarById(avatarId));
player.getTeamManager().getActiveTeam().add(entity);
Avatar avatar = player.getAvatars().getAvatarById(avatarId);
if (avatar == null) {
if (player.getTeamManager().isUseTrialTeam()) {
avatar = player.getTeamManager().getTrialAvatars().get(avatarId);
}
if (avatar == null) continue;
}
player.getTeamManager().getActiveTeam().add(new EntityAvatar(player.getScene(), avatar));
}
// Limit character index in case its out of bounds

View File

@ -0,0 +1,59 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.packet.send.PacketEnterTrialAvatarActivityDungeonRsp;
import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.net.proto.EnterTrialAvatarActivityDungeonReqOuterClass.EnterTrialAvatarActivityDungeonReq;
import emu.grasscutter.server.game.GameSession;
import lombok.val;
@Opcodes(PacketOpcodes.EnterTrialAvatarActivityDungeonReq)
public class HandlerEnterTrialAvatarActivityDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
EnterTrialAvatarActivityDungeonReq req = EnterTrialAvatarActivityDungeonReq.parseFrom(payload);
val playerData = session.getPlayer().getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerData == null) {
session.getPlayer().sendPacket(new PacketEnterTrialAvatarActivityDungeonRsp(
req.getActivityId(),
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
val handler = (TrialAvatarActivityHandler) playerData.get().getActivityHandler();
if (handler == null) {
session.getPlayer().sendPacket(new PacketEnterTrialAvatarActivityDungeonRsp(
req.getActivityId(),
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
// TODO, not sure if this will cause problem in MP, since we are entering trial activity dungeon
session.getPlayer().sendPacket(new PacketScenePlayerLocationNotify(session.getPlayer().getScene())); // official does send this
if (!handler.enterTrialDungeon(session.getPlayer(), req.getTrialAvatarIndexId(), req.getEnterPointId())) {
session.getPlayer().sendPacket(new PacketEnterTrialAvatarActivityDungeonRsp(
req.getActivityId(),
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
session.getPlayer().sendPacket(new PacketEnterTrialAvatarActivityDungeonRsp(
req.getActivityId(),
req.getTrialAvatarIndexId(),
Retcode.RET_SUCC_VALUE));
}
}

View File

@ -1,15 +1,27 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
import lombok.val;
@Opcodes(PacketOpcodes.PlayerQuitDungeonReq)
public class HandlerPlayerQuitDungeonReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
val playerData = session.getPlayer().getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerData != null) {
val handler = (TrialAvatarActivityHandler) playerData.get().getActivityHandler();
if (handler != null) {
handler.unsetTrialAvatarTeam(session.getPlayer());
}
}
session.getPlayer().getServer().getDungeonSystem().exitDungeon(session.getPlayer());
}

View File

@ -1,5 +1,7 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
@ -7,19 +9,41 @@ import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketPostEnterSceneRsp;
import lombok.val;
@Opcodes(PacketOpcodes.PostEnterSceneReq)
public class HandlerPostEnterSceneReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var sceneId = session.getPlayer().getSceneId();
var questManager = session.getPlayer().getQuestManager();
val sceneId = session.getPlayer().getSceneId();
val questManager = session.getPlayer().getQuestManager();
switch (session.getPlayer().getScene().getSceneType()){
case SCENE_ROOM -> questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_ROOM, sceneId,0);
case SCENE_WORLD -> {
questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_MY_WORLD, sceneId);
questManager.queueEvent(QuestContent.QUEST_CONTENT_ENTER_MY_WORLD_SCENE, sceneId);
}
case SCENE_DUNGEON -> {
val dungeonManager = session.getPlayer().getScene().getDungeonManager();
if (dungeonManager != null) {
dungeonManager.startDungeon();
if (dungeonManager.getDungeonData() != null) {
switch (dungeonManager.getDungeonData().getPlayType()) {
case DUNGEON_PLAY_TYPE_TRIAL_AVATAR -> {
val playerData = session.getPlayer().getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerData != null) {
val handler = (TrialAvatarActivityHandler) playerData.get().getActivityHandler();
if (handler != null) {
handler.setupTrialAvatarTeam(session.getPlayer());
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,54 @@
package emu.grasscutter.server.packet.recv;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.net.packet.Opcodes;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.packet.PacketHandler;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.net.proto.ReceivedTrialAvatarActivityRewardReqOuterClass.ReceivedTrialAvatarActivityRewardReq;
import emu.grasscutter.server.packet.send.PacketReceivedTrialAvatarActivityRewardRsp;
import lombok.val;
@Opcodes(PacketOpcodes.ReceivedTrialAvatarActivityRewardReq)
public class HandlerReceivedTrialAvatarActivityRewardReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
ReceivedTrialAvatarActivityRewardReq req = ReceivedTrialAvatarActivityRewardReq.parseFrom(payload);
val playerData = session.getPlayer().getActivityManager()
.getPlayerActivityDataByActivityType(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR);
if (playerData == null) {
session.getPlayer().sendPacket(new PacketReceivedTrialAvatarActivityRewardRsp(
5002, // trial activity id
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
val handler = (TrialAvatarActivityHandler) playerData.get().getActivityHandler();
if (handler == null) {
session.getPlayer().sendPacket(new PacketReceivedTrialAvatarActivityRewardRsp(
5002, // trial activity id
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
if (!handler.getReward(session.getPlayer(), playerData.get(), req.getTrialAvatarIndexId())) {
session.getPlayer().sendPacket(new PacketReceivedTrialAvatarActivityRewardRsp(
5002, // trial activity id
req.getTrialAvatarIndexId(),
Retcode.RET_FAIL_VALUE));
return;
}
session.getPlayer().sendPacket(new PacketReceivedTrialAvatarActivityRewardRsp(
5002, // trial activity id
req.getTrialAvatarIndexId(),
Retcode.RET_SUCC_VALUE));
}
}

View File

@ -18,13 +18,12 @@ public class PacketAvatarTeamUpdateNotify extends BasePacket {
super(PacketOpcodes.AvatarTeamUpdateNotify);
AvatarTeamUpdateNotify.Builder proto = AvatarTeamUpdateNotify.newBuilder();
if (player.getTeamManager().getTrialTeamGuid() != null && player.getTeamManager().getTrialTeamGuid().size() > 0){
proto.addAllTempAvatarGuidList(player.getTeamManager().getTrialTeamGuid().values().stream().toList());
} else{
for (Entry<Integer, TeamInfo> entry : player.getTeamManager().getTeams().entrySet()) {
TeamInfo teamInfo = entry.getValue();
proto.putAvatarTeamMap(entry.getKey(), teamInfo.toProto(player));
}
if (player.getTeamManager().isUseTrialTeam()) {
proto.addAllTempAvatarGuidList(player.getTeamManager().getActiveTeam().stream()
.map(x -> x.getAvatar().getGuid()).toList());
} else {
player.getTeamManager().getTeams().entrySet().stream()
.forEach(e -> proto.putAvatarTeamMap(e.getKey(), e.getValue().toProto(player)));
}
this.setData(proto);
}

View File

@ -13,7 +13,8 @@ public class PacketDungeonChallengeBeginNotify extends BasePacket {
DungeonChallengeBeginNotify proto = DungeonChallengeBeginNotify.newBuilder()
.setChallengeId(challenge.getChallengeId())
.setChallengeIndex(challenge.getChallengeIndex())
.setGroupId(challenge.getGroup().id)
// it will cause bug for trial avatar activity, will fix it in the next commit
// .setGroupId(challenge.getGroup().id)
.addAllParamList(challenge.getParamList())
.build();

View File

@ -0,0 +1,16 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.EnterTrialAvatarActivityDungeonRspOuterClass.EnterTrialAvatarActivityDungeonRsp;
public class PacketEnterTrialAvatarActivityDungeonRsp extends BasePacket {
public PacketEnterTrialAvatarActivityDungeonRsp(int activityId, int trialAvatarIndexId, int retcodeVal) {
super(PacketOpcodes.EnterTrialAvatarActivityDungeonRsp);
this.setData(EnterTrialAvatarActivityDungeonRsp.newBuilder()
.setActivityId(activityId)
.setTrialAvatarIndexId(trialAvatarIndexId)
.setRetcode(retcodeVal));
}
}

View File

@ -0,0 +1,18 @@
package emu.grasscutter.server.packet.send;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.net.proto.ReceivedTrialAvatarActivityRewardRspOuterClass.ReceivedTrialAvatarActivityRewardRsp;
public class PacketReceivedTrialAvatarActivityRewardRsp extends BasePacket {
public PacketReceivedTrialAvatarActivityRewardRsp(int activityId, int trialAvatarId, int retcodeVal) {
super(PacketOpcodes.ReceivedTrialAvatarActivityRewardRsp);
this.setData(ReceivedTrialAvatarActivityRewardRsp.newBuilder()
.setActivityId(activityId)
.setTrialAvatarIndexId(trialAvatarId)
.setRetcode(retcodeVal)
.build());
}
}