Start moving some models to AnimeGameModels and implement small things depeding on them

* Add rewards based on excel for ScenePointUnlock
* Properly handle multi level city/statue updates
Use animegamemodels for the following:
* Activity
  * ActivityCondExcelConfigData -> ActivityCondData
  * ActivityData
  * ActivityWatcherData
* Avatar
  * AvatarCostumeData
  * AvatarCurveData
  * AvatarData (and moved caching to its own model)
  * AvatarFettersLevelData
  * AvatarFlycloakData
  * AvatarLevelData
* City
  * CityData
  * StatuePromoteData -> CityLevelUpData
* Dungeon
  * DungeonData
  * DungeonPassConfigData
* Quest
  * TriggerExcelConfigData -> TriggerData
* Rewards
  * RewardData
  * TowerRewardData
* Scene
  * SceneData
  * SceneTagData -> SceneTagConfigData
* Shop
  * ActivityShopData
  * ShopGoodsData
  * ShopRefreshType
  * ShopType
* World
  * WorldAreaData
  * WorldLevelData
This commit is contained in:
hartie95 2024-02-17 21:15:56 +01:00
parent 95b26b5ba4
commit f6756d6621
133 changed files with 1270 additions and 1850 deletions

View File

@ -95,6 +95,9 @@ dependencies {
implementation("org.anime_game_servers.multi_proto:gi-jvm:0.2.32")
implementation("org.anime_game_servers.data_models:GIData:0.2")
implementation("org.anime_game_servers.data_models:loader-jvm:0.2")
implementation group: 'com.esotericsoftware', name : 'reflectasm', version: '1.11.9'
implementation group: 'com.github.davidmoten', name : 'rtree-multi', version: '0.1'
@ -255,3 +258,8 @@ public final class BuildConfig {
}
task generateActivityConditions(type: GenerateActivityConditions)
// For terminal interacting in IDEA running
run {
standardInput = System.in
}

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -7,16 +7,8 @@ import emu.grasscutter.command.CommandMap;
import emu.grasscutter.command.DefaultPermissionHandler;
import emu.grasscutter.command.PermissionHandler;
import emu.grasscutter.config.ConfigContainer;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.TowerFloorData;
import emu.grasscutter.data.excels.TowerLevelData;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.avatar.TowerAvatar;
import emu.grasscutter.game.dungeons.dungeon_entry.PlayerDungeonExitInfo;
import emu.grasscutter.plugin.PluginManager;
import emu.grasscutter.plugin.api.ServerHook;
import emu.grasscutter.server.game.GameServer;
@ -50,7 +42,6 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.*;
import java.util.Calendar;
import java.util.Optional;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.Language.translate;
@ -119,7 +110,11 @@ public final class Grasscutter {
ResourceLoader.loadAll();
// Generate handbooks.
Tools.createGmHandbooks();
try {
Tools.createGmHandbooks();
} catch (Exception e) {
getLogger().error("Failed to generate GM handbooks.", e);
}
// Generate gacha mappings.
Tools.generateGachaMappings();

View File

@ -4,7 +4,7 @@ import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.custom.AvatarDataCache;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.ReliquaryAffixData;
@ -17,6 +17,8 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.utils.SparseSet;
import lombok.Setter;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarData;
import java.util.ArrayList;
import java.util.List;
@ -62,7 +64,7 @@ public final class GiveCommand implements CommandHandler {
public int mainPropId = -1;
public List<Integer> appendPropIdList;
public ItemData data;
public AvatarData avatarData;
public AvatarDataCache avatarData;
public GiveAllType giveAllType = GiveAllType.NONE;
}
@ -103,9 +105,9 @@ public final class GiveCommand implements CommandHandler {
}
param.data = GameData.getItemDataMap().get(param.id);
if ((param.id > 10_000_000) && (param.id < 12_000_000))
param.avatarData = GameData.getAvatarDataMap().get(param.id);
param.avatarData = GameData.getAvatarInfoCacheMap().get(param.id);
else if ((param.id > 1000) && (param.id < 1100))
param.avatarData = GameData.getAvatarDataMap().get(param.id - 1000 + 10_000_000);
param.avatarData = GameData.getAvatarInfoCacheMap().get(param.id - 1000 + 10_000_000);
isRelic = ((param.data != null) && (param.data.getItemType() == ItemType.ITEM_RELIQUARY));
if (!isRelic && !args.isEmpty() && (param.amount == 1)) { // A concession for the people that truly hate [x<amount>].
@ -218,7 +220,7 @@ public final class GiveCommand implements CommandHandler {
return makeAvatar(param.avatarData, param.lvl, Avatar.getMinPromoteLevel(param.lvl), param.constellation, param.skillLevel);
}
private static Avatar makeAvatar(AvatarData avatarData, int level, int promoteLevel, int constellation, int skillLevel) {
private static Avatar makeAvatar(AvatarDataCache avatarData, int level, int promoteLevel, int constellation, int skillLevel) {
Avatar avatar = new Avatar(avatarData);
avatar.setLevel(level);
avatar.setPromoteLevel(promoteLevel);
@ -232,7 +234,7 @@ public final class GiveCommand implements CommandHandler {
private static void giveAllAvatars(Player player, GiveItemParameters param) {
int promoteLevel = Avatar.getMinPromoteLevel(param.lvl);
if (param.constellation < 0 || param.constellation > 6) param.constellation = 6; // constellation's default is -1 so if no parameters set for constellations it'll automatically be 6
for (AvatarData avatarData : GameData.getAvatarDataMap().values()) {
for (val avatarData : GameData.getAvatarInfoCacheMap().values()) {
int id = avatarData.getId();
if (id < 10000002 || id >= 11000000) continue; // Exclude test avatars
// Don't try to add each avatar to the current team

View File

@ -3,8 +3,8 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import lombok.val;
import java.util.List;
@ -24,15 +24,15 @@ public final class ResetConstCommand implements CommandHandler {
targetPlayer.getAvatars().forEach(this::resetConstellation);
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all"));
} else {
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
val entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
if (entity == null) {
return;
}
Avatar avatar = entity.getAvatar();
val avatar = entity.getAvatar();
this.resetConstellation(avatar);
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName()));
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.success", avatar.getAvatarData().getBaseName()));
}
}

View File

@ -3,12 +3,9 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.game.world.World;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import lombok.val;
import java.util.List;
@ -34,11 +31,11 @@ public final class SetConstCommand implements CommandHandler {
}
// If it's either empty or anything else other than "all" just do normal setConstellation
if (args.size() == 1) {
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
val entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
if (entity == null) return;
Avatar avatar = entity.getAvatar();
val avatar = entity.getAvatar();
this.setConstellation(targetPlayer, avatar, constLevel);
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.success", avatar.getAvatarData().getName(), constLevel);
CommandHandler.sendTranslatedMessage(sender, "commands.setConst.success", avatar.getAvatarData().getBaseName(), constLevel);
return;
}
// Check if there's an additional argument which is "all", if it does then go setAllConstellation
@ -53,7 +50,7 @@ public final class SetConstCommand implements CommandHandler {
}
private void setConstellation(Player player, Avatar avatar, int constLevel) {
int currentConstLevel = avatar.getCoreProudSkillLevel();
val currentConstLevel = avatar.getCoreProudSkillLevel();
avatar.forceConstellationLevel(constLevel);
// force player to reload scene when necessary
@ -79,9 +76,9 @@ public final class SetConstCommand implements CommandHandler {
}
private void reloadScene(Player player) {
World world = player.getWorld();
Scene scene = player.getScene();
Position pos = player.getPosition();
val world = player.getWorld();
val scene = player.getScene();
val pos = player.getPosition();
world.transferPlayerToScene(player, 1, pos, null);
world.transferPlayerToScene(player, scene.getId(), pos, null);
scene.broadcastPacket(new PacketSceneEntityAppearNotify(player));

View File

@ -36,7 +36,7 @@ public final class SetFetterLevelCommand implements CommandHandler {
avatar.setFetterLevel(fetterLevel);
if (fetterLevel != 10) {
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getNeedExp());
}
avatar.save();

View File

@ -5,12 +5,12 @@ import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarPlayerData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
import emu.grasscutter.utils.JsonUtils;
import java.util.List;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import static emu.grasscutter.utils.Language.translate;

View File

@ -0,0 +1,8 @@
package emu.grasscutter.data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoResource {
}

View File

@ -1,5 +1,8 @@
package emu.grasscutter.data;
import java.lang.reflect.Field;
import java.util.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.config.*;
@ -12,6 +15,13 @@ import emu.grasscutter.data.custom.TrialAvatarActivityCustomData;
import emu.grasscutter.data.custom.TrialAvatarCustomData;
import emu.grasscutter.data.excels.*;
import emu.grasscutter.data.server.*;
import emu.grasscutter.data.binout.routes.Route;
import emu.grasscutter.data.custom.*;
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.game.dungeons.DungeonDropEntry;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.game.quest.QuestEncryptionKey;
@ -20,12 +30,32 @@ import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.*;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Tolerate;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.custom.activity.ActivityExtraInfo;
import org.anime_game_servers.game_data_models.gi.custom.weather.WeatherMapping;
import org.anime_game_servers.game_data_models.gi.data.activity.*;
import org.anime_game_servers.game_data_models.gi.data.city.CityData;
import org.anime_game_servers.game_data_models.gi.data.city.CityLevelUpData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.*;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarCurveData;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarData;
import org.anime_game_servers.gi_lua.models.quest.QuestData;
import org.anime_game_servers.gi_lua.models.quest.RewindData;
import org.anime_game_servers.gi_lua.models.scene.DummyPoint;
import org.anime_game_servers.gi_lua.models.scene.SceneGroupReplacement;
import org.anime_game_servers.game_data_models.gi.data.quest.TriggerData;
import org.anime_game_servers.game_data_models.gi.data.rewards.TransPointRewardData;
import org.anime_game_servers.game_data_models.gi.data.rewards.RewardData;
import org.anime_game_servers.game_data_models.gi.data.rewards.TowerRewardData;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneData;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneTagConfigData;
import org.anime_game_servers.game_data_models.gi.data.scene.WorldAreaConfigData;
import org.anime_game_servers.game_data_models.gi.data.shop.ShopGoodsData;
import org.anime_game_servers.game_data_models.gi.data.talks.TalkData;
import org.anime_game_servers.game_data_models.gi.data.watcher.ActivityWatcherData;
import org.anime_game_servers.game_data_models.gi.data.world.WorldLevelData;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
@ -46,29 +76,32 @@ public class GameData {
@Getter private static final Int2ObjectMap<DungeonEntries> dungeonEntriesMap = new Int2ObjectOpenHashMap<>();
protected static final Map<String, AbilityData> abilityDataMap = new HashMap<>();
protected static final Int2ObjectMap<ScenePointEntry> scenePointEntryMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<MainQuestData> mainQuestData = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<MainQuestData> mainQuestDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<QuestEncryptionKey> questsKeys = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<SceneNpcBornData> npcBornData = new Int2ObjectOpenHashMap<>();
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
@Getter private static final Int2ObjectMap<SceneNpcBornData> npcBornData = new Int2ObjectOpenHashMap<>();
@Getter private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
@QuickAccessCache @Getter private static final Map<Long, String> textHashMap = new HashMap<>();
// ExcelConfigs
@Getter private static final Int2ObjectMap<ActivityCondExcelConfigData> activityCondExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonPassConfigData> dungeonPassConfigDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityCondData> activityCondExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<DungeonPassData> dungeonPassConfigDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonChallengeConfigData> dungeonChallengeConfigDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<Int2ObjectMap<Route>> sceneRouteData = new Int2ObjectOpenHashMap<>();
@Getter private static final ArrayList<CodexReliquaryData> codexReliquaryArrayList = new ArrayList<>();
@Getter private static final Int2ObjectMap<ActivityData> activityDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ActivityShopData> activityShopDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ActivityWatcherData> activityWatcherDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataItemIdMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityData> activityDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityShopOverallData> activityShopDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityShopSheetData> activityShopSheetDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityWatcherData> activityWatcherDataMap = new Int2ObjectOpenHashMap<>();
@QuickAccessCache @Getter private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataItemIdMap = new Int2ObjectLinkedOpenHashMap<>();
@QuickAccessCache @Getter private static final Int2ObjectMap<AvatarDataCache> avatarInfoCacheMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarCostumeData> avatarCostumeDataMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarReplaceCostumeData> avatarReplaceCostumeDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarData> avatarDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarFetterLevelData> avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarFlycloakData> avatarFlycloakDataMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarLevelData> avatarLevelDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarData> avatarDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarFettersLevelData> avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarFlycloakData> avatarFlycloakDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<AvatarLevelData> avatarLevelDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarSkillData> avatarSkillDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarSkillDepotData> avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarTalentData> avatarTalentDataMap = new Int2ObjectOpenHashMap<>();
@ -81,7 +114,8 @@ public class GameData {
@Getter private static final Int2ObjectMap<BlossomSectionOrderData> blossomSectionOrderDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<BuffData> buffDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ChapterData> chapterDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<CityData> cityDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<CityData> cityDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<CityLevelUpData> cityLevelUpDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<CodexAnimalData> codexAnimalDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<CodexMaterialData> codexMaterialDataIdMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<CodexQuestData> codexQuestDataIdMap = new Int2ObjectOpenHashMap<>();
@ -93,7 +127,7 @@ public class GameData {
@Getter private static final Int2ObjectMap<CookRecipeData> cookRecipeDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<CompoundData> compoundDataMap=new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonElementChallengeData> dungeonElementChallengeDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonEntryData> dungeonEntryDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DungeonRosterData> dungeonRosterDataMap = new Int2ObjectOpenHashMap<>();
@ -126,35 +160,35 @@ public class GameData {
@Getter private static final Int2ObjectMap<ReliquaryAffixData> reliquaryAffixDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ReliquaryMainPropData> reliquaryMainPropDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ReliquarySetData> reliquarySetDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<SceneTagData> sceneTagDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<SceneTagConfigData> sceneTagConfigDataMap = new Int2ObjectLinkedOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<TalkData> talkDataMap = new Int2ObjectLinkedOpenHashMap<>();
@Getter private static final Int2ObjectMap<TowerBuffData> towerBuffDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<TowerRewardData> towerRewardDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource private static final Int2ObjectMap<TowerRewardData> towerRewardDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TowerScheduleData> towerScheduleDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TrialAvatarData> trialAvatarDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TrialAvatarActivityData> trialAvatarActivityDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TrialAvatarActivityDataData> trialAvatarActivityDataDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TrialAvatarTemplateData> trialAvatarTemplateDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TrialReliquaryData> trialReliquaryDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<TriggerExcelConfigData> triggerExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Map<String, TriggerExcelConfigData> triggerDataByNameMap = new HashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<TriggerData> triggerExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
@QuickAccessCache @Getter private static final Map<String, TriggerData> triggerDataByNameMap = new HashMap<>();
@Getter private static final Int2ObjectMap<WeaponCurveData> weaponCurveDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WeaponLevelData> weaponLevelDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WeaponPromoteData> weaponPromoteDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<StatuePromoteData> statuePromoteDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WeatherData> weatherDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WeatherTemplateData> weatherTemplateDataMap = new Int2ObjectOpenHashMap<>(); //Unused
@Getter private static final Map<String, WeatherTemplateData> weatherTemplateDataByNameMap = new HashMap<>();
@Getter private static final Int2ObjectMap<WorldAreaData> worldAreaDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<AvatarPromoteData> avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<WorldAreaConfigData> worldAreaDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<AvatarPromoteData> avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ReliquaryLevelData> reliquaryLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<RewindData> rewindDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<QuestData> teleportDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<Map<String, DummyPoint>> dummyPointMap = new Int2ObjectOpenHashMap<>();
@ -167,18 +201,22 @@ public class GameData {
private static final Int2ObjectMap<CodexViewpointData> codexViewpointDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<List<DungeonDropEntry>> dungeonDropDataMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<TransPointRewardData> transPointRewardData = new Int2ObjectOpenHashMap<>();
@Getter @Setter private static ConfigGlobalCombat configGlobalCombat = null;
// from scripts
@Getter private static final Int2ObjectMap<SceneGroupReplacement> groupReplacements = new Int2ObjectOpenHashMap<>();
// Custom community server resources
@Getter private static final Int2ObjectMap<GadgetMapping> gadgetMappingMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<SubfieldMapping> subfieldMappingMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DropSubfieldMapping> dropSubfieldMappingMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<DropTableExcelConfigData> dropTableExcelConfigDataMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<MonsterMapping> monsterMappingMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<ActivityCondGroup> activityCondGroupMap = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<SceneGroupReplacement> groupReplacements = new Int2ObjectOpenHashMap<>();
@Getter private static final Int2ObjectMap<WeatherMapping> weatherMappingMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityCondGroupData> activityCondGroupMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<ActivityExtraInfo> activityExtraInfoMap = new Int2ObjectOpenHashMap<>();
@AutoResource @Getter private static final Int2ObjectMap<WeatherMapping> weatherMappingMap = new Int2ObjectOpenHashMap<>();
// Cache
@Getter private static final IntList scenePointIdList = new IntArrayList();
@ -188,7 +226,7 @@ public class GameData {
@Getter private static final Map<String, ConfigLevelEntity> configLevelEntityDataMap = new HashMap<>();
@Getter private static final Map<String, GuideTriggerData> guideTriggerDataStringMap = new HashMap<>();
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
@QuickAccessCache private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
protected static Int2ObjectMap<IntSet> proudSkillGroupLevels = new Int2ObjectOpenHashMap<>();
protected static Int2IntMap proudSkillGroupMaxLevels = new Int2IntOpenHashMap();
protected static Int2ObjectMap<IntSet> avatarSkillLevels = new Int2ObjectOpenHashMap<>();
@ -201,28 +239,17 @@ public class GameData {
@Getter private static final Map<Integer, List<WeatherAreaPointData>> weatherAreaPointData = new HashMap<>();
@Getter private static final Map<Integer, List<ScenePointArrayData>> scenePointArrayData = new HashMap<>();
// Getters with wrong names, remove later
@Deprecated(forRemoval = true) public static Int2ObjectMap<CodexReliquaryData> getcodexReliquaryIdMap() {return codexReliquaryDataIdMap;}
@Deprecated(forRemoval = true) public static Int2ObjectMap<DungeonEntryData> getDungeonEntryDatatMap() {return dungeonEntryDataMap;}
@Deprecated(forRemoval = true) @Tolerate public static ArrayList<CodexReliquaryData> getcodexReliquaryArrayList() {return codexReliquaryArrayList;}
// Getters with different names that stay for now
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {return mainQuestData;}
public static Int2ObjectMap<QuestEncryptionKey> getMainQuestEncryptionMap() {return questsKeys;}
public static Int2ObjectMap<SceneNpcBornData> getSceneNpcBornData() {return npcBornData;}
public static Map<String, AbilityEmbryoEntry> getAbilityEmbryoInfo() {return abilityEmbryos;}
// Getters that get values rather than containers. If Lombok ever gets syntactic sugar for this, we should adopt that.
public static AbilityData getAbilityData(String abilityName) {return abilityDataMap.get(abilityName);}
public static IntSet getAvatarSkillLevels(int avatarSkillId) {return avatarSkillLevels.get(avatarSkillId);}
public static IntSet getProudSkillGroupLevels(int proudSkillGroupId) {return proudSkillGroupLevels.get(proudSkillGroupId);}
@Nullable public static AbilityData getAbilityData(String abilityName) {return abilityDataMap.get(abilityName);}
@Nullable public static IntSet getAvatarSkillLevels(int avatarSkillId) {return avatarSkillLevels.get(avatarSkillId);}
public static int getProudSkillGroupMaxLevel(int proudSkillGroupId) {return proudSkillGroupMaxLevels.getOrDefault(proudSkillGroupId, 0);}
@Nullable public static AbilityEmbryoEntry getAbilityEmbryo(String name) {return abilityEmbryos.get(name);}
@Nullable public static SceneNpcBornData getSceneNpcBornData(int npcId) {return npcBornData.get(npcId);}
// Multi-keyed getters
public static AvatarPromoteData getAvatarPromoteData(int promoteId, int promoteLevel) {
return avatarPromoteDataMap.get((promoteId << 8) + promoteLevel);
}
public static TowerRewardData getTowerRewardData(int levelIndex, int floorIndex) {
return towerRewardDataMap.get((levelIndex << 4) + floorIndex);
}
@ -231,8 +258,8 @@ public class GameData {
return weaponPromoteDataMap.get((promoteId << 8) + promoteLevel);
}
public static StatuePromoteData getStatuePromoteData(int cityId, int promoteLevel) {
return statuePromoteDataMap.get((cityId << 8) + promoteLevel);
public static CityLevelUpData getCityLevelUpData(int cityId, int promoteLevel) {
return cityLevelUpDataMap.get(CityLevelUpData.getKey(cityId, promoteLevel));
}
public static ReliquaryLevelData getRelicLevelData(int rankLevel, int level) {
@ -249,7 +276,7 @@ public class GameData {
}
public static int getAvatarFetterLevelExpRequired(int level) {
return Optional.ofNullable(avatarFetterLevelDataMap.get(level)).map(AvatarFetterLevelData::getExp).orElse(0);
return Optional.ofNullable(avatarFetterLevelDataMap.get(level)).map(AvatarFettersLevelData::getNeedExp).orElse(0);
}
public static int getRelicExpRequired(int rankLevel, int level) {
@ -275,7 +302,6 @@ public class GameData {
}
public static int getWeaponExpRequired(int rankLevel, int level) {
WeaponLevelData levelData = weaponLevelDataMap.get(level);
if (levelData == null) {
@ -304,9 +330,9 @@ public class GameData {
public static Map<Integer, List<ShopGoodsData>> getShopGoodsDataEntries() {
if (shopGoods.isEmpty()) {
shopGoodsDataMap.forEach((k, v) -> {
if (!shopGoods.containsKey(v.getShopType()))
shopGoods.put(v.getShopType(), new ArrayList<>());
shopGoods.get(v.getShopType()).add(v);
val shopTypeId = v.getShopType().getIntKey();
shopGoods.computeIfAbsent(shopTypeId, key -> new ArrayList<>())
.add(v);
});
}
@ -335,11 +361,18 @@ public class GameData {
return beginCondQuestMap.get(SubQuestData.questConditionKey(questCond, param0, questStr));
}
public static TriggerExcelConfigData getQuestTriggerDataByName(int groupId, String triggerName){
public static TriggerData getQuestTriggerDataByName(int groupId, String triggerName){
return triggerDataByNameMap.get(groupId + triggerName);
}
public static void putQuestTriggerDataCache(TriggerData trigger){
triggerDataByNameMap.put(trigger.getGroupId()+trigger.getTriggerName(), trigger);
}
public static CodexViewpointData getViewCodexByGroupdCfg(int groupId, int cfgId){
public static void putAvatarCostumeDataCache(AvatarCostumeData data){
avatarCostumeDataItemIdMap.put(data.getItemId(), data);
}
public static CodexViewpointData getViewCodexByGroupdCfg(int groupId, int cfgId) {
return codexViewpointDataIdMap.get(CodexViewpointData.getViewpointId(groupId, cfgId));
}
}

View File

@ -0,0 +1,8 @@
package emu.grasscutter.data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface QuickAccessCache {
}

View File

@ -2,6 +2,8 @@ package emu.grasscutter.data;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.Loggers;
import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
@ -13,14 +15,18 @@ import emu.grasscutter.data.common.ScenePointArrayData;
import emu.grasscutter.data.common.WeatherAreaPointData;
import emu.grasscutter.data.common.quest.MainQuestData;
import emu.grasscutter.data.common.quest.SubQuestData;
import emu.grasscutter.data.custom.AvatarDataCache;
import emu.grasscutter.data.custom.TrialAvatarActivityCustomData;
import emu.grasscutter.data.custom.TrialAvatarCustomData;
import emu.grasscutter.data.excels.TrialAvatarActivityDataData;
import emu.grasscutter.data.server.*;
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.game.ability.Ability;
import emu.grasscutter.game.dungeons.DungeonDrop;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.game.dungeons.enums.DungeonType;
import emu.grasscutter.game.managers.blossom.BlossomConfig;
import emu.grasscutter.game.quest.QuestEncryptionKey;
import emu.grasscutter.game.quest.enums.QuestCond;
@ -38,17 +44,30 @@ import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import kotlin.Unit;
import kotlinx.serialization.json.Json;
import kotlinx.serialization.json.JsonKt;
import lombok.val;
import org.anime_game_servers.core.base.interfaces.IntKey;
import org.anime_game_servers.game_data_models.gi.GIDataModelRegistry;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonType;
import org.anime_game_servers.game_data_models.gi.helpers.TextHashUtilsKt;
import org.anime_game_servers.game_data_models.loader.*;
import org.anime_game_servers.gi_lua.models.loader.SceneReplacementScriptLoadParams;
import org.anime_game_servers.gi_lua.models.loader.ShardQuestScriptLoadParams;
import org.anime_game_servers.gi_lua.models.quest.QuestData;
import org.anime_game_servers.gi_lua.models.quest.RewindData;
import org.anime_game_servers.gi_lua.models.scene.SceneGroupReplacement;
import org.reflections.Reflections;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.*;
import org.slf4j.Logger;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
@ -69,10 +88,36 @@ public class ResourceLoader {
private static final Set<String> loadedResources = new CopyOnWriteArraySet<>();
private static DefaultDataLoader dataLoader;
private static void initDataLoader() {
val json = JsonKt.Json(Json.Default, (jsonBuilder -> {
jsonBuilder.setIgnoreUnknownKeys(true);
jsonBuilder.setLenient(true);
jsonBuilder.setAllowComments(true);
jsonBuilder.setAllowTrailingComma(true);
return Unit.INSTANCE;
}));
val jsonParser = new JsonDataParser(json);
dataLoader = new DefaultDataLoader();
// register GI Models
dataLoader.registerDataClassSource(GIDataModelRegistry.INSTANCE);
// add default json parser
dataLoader.setParser(FileType.JSON, jsonParser);
// add paths for each type
val resourcePath = new JvmPathFile(getResourcePath(""));
dataLoader.addFolderTypeSource(FolderType.EXCEL, resourcePath);
dataLoader.addFolderTypeSource(FolderType.BINOUT, resourcePath);
dataLoader.addFolderTypeSource(FolderType.GENERATED, resourcePath);
dataLoader.addFolderTypeSource(FolderType.CUSTOM, resourcePath);
}
// Get a list of all resource classes, sorted by loadPriority
public static List<Class<?>> getResourceDefClasses() {
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
Set<?> classes = Grasscutter.reflector.getSubTypesOf(GameResource.class);
List<Class<?>> classList = new ArrayList<>(classes.size());
classes.forEach(o -> {
@ -89,8 +134,7 @@ public class ResourceLoader {
// Get a list containing sets of all resource classes, sorted by loadPriority
protected static List<Set<Class<?>>> getResourceDefClassesPrioritySets() {
val reflections = new Reflections(ResourceLoader.class.getPackage().getName());
val classes = reflections.getSubTypesOf(GameResource.class);
val classes = Grasscutter.reflector.getSubTypesOf(GameResource.class);
val priorities = ResourceType.LoadPriority.getInOrder();
logger.debug("Priorities are {}", priorities);
val map = new LinkedHashMap<ResourceType.LoadPriority, Set<Class<?>>>(priorities.size());
@ -111,6 +155,8 @@ public class ResourceLoader {
if (loadedAll) return;
logger.info(translate("messages.status.resources.loading"));
initDataLoader();
loadConfigData();
// Load ability lists
loadAbilityEmbryos();
@ -119,6 +165,8 @@ public class ResourceLoader {
loadAbilityModifiers();
// Load resources
loadResources(true);
loadExcel();
initExcelCaches();
// Process into depots
GameDepot.load();
// Load spawn data and quests
@ -142,9 +190,7 @@ public class ResourceLoader {
loadScriptData();
loadGadgetMappings();
loadSubfieldMappings();
loadWeatherMappings();
loadMonsterMappings();
loadActivityCondGroups();
loadTrialAvatarCustomData();
loadGlobalCombatConfig();
EntityControllerScriptManager.load();
@ -152,6 +198,90 @@ public class ResourceLoader {
loadedAll = true;
}
public static void loadExcel() {
getAnimeGameModelsMaps();
}
public static void getAnimeGameModelsMaps() {
val fields = GameData.class.getDeclaredFields();
Arrays.stream(fields).parallel()
.filter(field -> field.getAnnotation(AutoResource.class) != null && field.getAnnotation(QuickAccessCache.class) == null)
.forEach(field -> {
try {
val type = field.getType();
if (type.equals(Int2ObjectMap.class)) {
loadInt2ObjectMap(field);
} else if (Map.class.isAssignableFrom(type)) {
loadGenericMap(field);
} else {
logger.info("Field {} is not a map", field.getName());
}
} catch (Exception e) {
logger.error("Error loading field {}", field.getName(), e);
}
});
}
private static void loadInt2ObjectMap(Field field) throws IllegalAccessException {
val arguments = ((ParameterizedType) field.getGenericType()).getActualTypeArguments();
if (arguments.length != 1)
throw new RuntimeException("expected 1 generic type argument for Int2ObjectMap");
val type = arguments[0];
if (!(type instanceof Class<?>)) {
return;
}
if (!(IntKey.class.isAssignableFrom((Class<?>) type))) {
return;
}
val targetClass = (Class<? extends IntKey>) arguments[0];
val list = dataLoader.loadListBlocking(targetClass);
if(list != null){
field.setAccessible(true);
val map = (Int2ObjectMap<Object>) field.get(null);
field.setAccessible(false);
list.forEach(value -> map.put(value.getIntKey(), value));
logger.error("loaded {} entries for {}", map.size(), targetClass.getName());
}
}
private static void loadGenericMap(Field field){
val genericType = field.getGenericType();
if(!(genericType instanceof ParameterizedType)){
return;
}
val arguments = ((ParameterizedType) genericType).getActualTypeArguments();
if (arguments.length != 2)
throw new RuntimeException("expected 2 generic type arguments for Map");
val keyType = arguments[0];
val valueType = arguments[1];
if (!(keyType instanceof Class<?>)) {
return;
}
if (!(valueType instanceof Class<?>)) {
return;
}
if(((Class<?>)valueType).getPackage().getName().contains("grasscutter")){
return;
}
if(keyType.equals(String.class)){
//TODO handle StringKey
}
}
public static void initExcelCaches(){
GameData.getTriggerExcelConfigDataMap().values().forEach(GameData::putQuestTriggerDataCache);
initAvatarCaches();
}
public static void initAvatarCaches(){
GameData.getAvatarCostumeDataMap().values().forEach(GameData::putAvatarCostumeDataCache);
GameData.getAvatarDataMap().forEach((id, data) -> GameData.getAvatarInfoCacheMap().put(id, new AvatarDataCache(data)));
}
public static void loadResources() {
loadResources(false);
}
@ -406,7 +536,7 @@ public class ResourceLoader {
}
for (AbilityEmbryoEntry entry : embryoList) {
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
GameData.getAbilityEmbryos().put(entry.getName(), entry);
}
}
@ -677,10 +807,10 @@ public class ResourceLoader {
}
data.setIndex(SceneIndexManager.buildIndex(3, data.getBornPosList(), item -> item.getPos().toPoint()));
GameData.getSceneNpcBornData().put(data.getSceneId(), data);
GameData.getNpcBornData().put(data.getSceneId(), data);
} catch (IOException ignored) {}
});
logger.debug("Loaded {} SceneNpcBornDatas.", GameData.getSceneNpcBornData().size());
logger.debug("Loaded {} SceneNpcBornDatas.", GameData.getNpcBornData().size());
} catch (IOException e) {
logger.error("Failed to load SceneNpcBorn folder.");
}
@ -714,6 +844,9 @@ public class ResourceLoader {
try {
val name = path.getFileName().toString().replace(".json", "");
targetMap.put(name, JsonUtils.loadToClass(path, configClass));
val textHashBase = "Data/_"+folderPath+name+".MiHoYoBinData";
val textHash = TextHashUtilsKt.getTextHash(textHashBase);
GameData.getTextHashMap().put(textHash, name);
} catch (Exception e) {
logger.error("failed to load {} entries for {}", className, path.toString(), e);
}
@ -900,18 +1033,6 @@ public class ResourceLoader {
}
}
private static void loadWeatherMappings() {
try {
val weatherMap = GameData.getWeatherMappingMap();
try {
JsonUtils.loadToList(getResourcePath("Server/WeatherMapping.json"), WeatherMapping.class).forEach(entry -> weatherMap.put(entry.getAreaId(), entry));;
} catch (IOException | NullPointerException ignored) {}
logger.debug("Loaded {} weather mappings.", weatherMap.size());
} catch (Exception e) {
logger.error("Unable to load weather mappings.", e);
}
}
private static void loadMonsterMappings() {
try {
val monsterMap = GameData.getMonsterMappingMap();
@ -924,18 +1045,6 @@ public class ResourceLoader {
}
}
private static void loadActivityCondGroups() {
try {
val gadgetMap = GameData.getActivityCondGroupMap();
try {
JsonUtils.loadToList(getResourcePath("Server/ActivityCondGroups.json"), ActivityCondGroup.class).forEach(entry -> gadgetMap.put(entry.getCondGroupId(), entry));
} catch (IOException | NullPointerException ignored) {}
logger.debug("Loaded {} ActivityCondGroups.", gadgetMap.size());
} catch (Exception e) {
logger.error("Unable to load ActivityCondGroups.", e);
}
}
private static void loadTrialAvatarCustomData() {
try {
String pathName = "TrialAvatar/";

View File

@ -0,0 +1,215 @@
package emu.grasscutter.data.custom;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.binout.config.ConfigEntityAvatar;
import emu.grasscutter.data.excels.AvatarPromoteData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.utils.Language;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Data;
import lombok.NonNull;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.entities.CreatureExcelConfig;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarCurveData;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarData;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.GrowthCurveType;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.WeaponType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Data
public class AvatarDataCache {
// baseData
private int avatarId;
private AvatarData avatarData;
private ConfigEntityAvatar configEntityAvatar;
private String baseName;
// abilities
private Map<Integer, AvatarSkillDepotData> skillDepots;
private int defaultSkillDepotId;
private IntList abilities;
private List<String> abilityNames = new ArrayList<>();
// stat/prop growth
private float[] hpGrowthCurve;
private float[] attackGrowthCurve;
private float[] defenseGrowthCurve;
// promote data
private Map<Integer, AvatarPromoteData> promoteData;
// friendship level
private List<Integer> fetters;
private int nameCardRewardId;
private int nameCardId;
private static Pattern baseNamePattern = Pattern.compile("ConfigAvatar_(.+)");
public AvatarDataCache(AvatarData data) {
this.avatarId = data.getId();
this.avatarData = data;
val configDataName = GameData.getTextHashMap().get(data.getCombatConfigHashJvm());
configEntityAvatar = GameData.getAvatarConfigData().get(configDataName);
initBaseName(configDataName);
initSkillDepotData();
initFriendshipData();
initAscensionData();
initGrowthCurveData();
rebuildAbilityEmbryo();
}
private void initBaseName(String configDataName){
if(configDataName!=null) {
val matcher = baseNamePattern.matcher(configDataName);
if (matcher.find()) {
this.baseName = matcher.group(1);
} else {
this.baseName = configDataName;
}
} else {
this.baseName = Language.getTextMapKey(avatarData.getNameTextMapHash()).get(0);
}
}
private void initSkillDepotData(){
defaultSkillDepotId = avatarData.getSkillDepotId();
var candDepotIds = avatarData.getCandSkillDepotIdsNonNull();
if (candDepotIds.isEmpty()) {
candDepotIds = List.of(defaultSkillDepotId);
}
this.skillDepots = candDepotIds.stream()
.map(depotId -> GameData.getAvatarSkillDepotDataMap().get(depotId))
.collect(Collectors.toMap(AvatarSkillDepotData::getId, depot -> depot));
}
private void initFriendshipData(){
this.fetters = GameData.getFetterDataEntries().get(this.avatarId);
if (GameData.getFetterCharacterCardDataMap().get(this.avatarId) != null) {
this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.avatarId).getRewardId();
}
if (GameData.getRewardDataMap().get(this.nameCardRewardId) != null) {
this.nameCardId = GameData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId();
}
}
private void initAscensionData(){
val promoteId = this.avatarData.getAvatarPromoteId();
this.promoteData = GameData.getAvatarPromoteDataMap().values().stream()
.filter(data -> data.getAvatarPromoteId() == promoteId)
.collect(Collectors.toMap(AvatarPromoteData::getPromoteLevel, data -> data));
}
private void initGrowthCurveData(){
int size = GameData.getAvatarCurveDataMap().size();
this.hpGrowthCurve = new float[size];
this.attackGrowthCurve = new float[size];
this.defenseGrowthCurve = new float[size];
for (val curveData : GameData.getAvatarCurveDataMap().values()) {
int level = curveData.getLevel() - 1;
for (CreatureExcelConfig.FightPropGrowConfig growCurve : this.avatarData.getPropGrowCurvesNonNull()) {
val targetMap = switch (growCurve.getTypeNonNull()) {
case FIGHT_PROP_BASE_HP:
yield this.hpGrowthCurve;
case FIGHT_PROP_BASE_ATTACK:
yield this.attackGrowthCurve;
case FIGHT_PROP_BASE_DEFENSE:
yield this.defenseGrowthCurve;
default:
yield null;
};
if (targetMap == null) {
Grasscutter.getLogger().warn("Unknown prop: {}", growCurve.getType());
continue;
}
targetMap[level] = getGrowthCurveValue(curveData, growCurve.getGrowCurve());
}
}
/*
for (PropGrowCurve growCurve : this.PropGrowCurves) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
this.growthCurveMap.put(prop.getId(), growCurve.getGrowCurve());
}
*/
}
public void rebuildAbilityEmbryo() {
// Cache abilities
AbilityEmbryoEntry info = GameData.getAbilityEmbryo(this.baseName);
if (info != null) {
this.abilities = new IntArrayList(info.getAbilities().length);
for (String ability : info.getAbilities()) {
this.abilities.add(Utils.abilityHash(ability));
abilityNames.add(ability);
}
}
}
public int getId(){
return avatarId;
}
private float getGrowthCurveValue(AvatarCurveData curveData, GrowthCurveType growthCurveType) {
val curveInfos = curveData.getCurveInfosNonNull();
return curveInfos.stream()
.filter(info -> growthCurveType.equals(info.getType()))
.findFirst().map(AvatarCurveData.GrowCurveInfo::getValue).orElse(1f);
}
public float getBaseHp(int level) {
val hpBase = avatarData.getHpBase();
try {
return hpBase * this.hpGrowthCurve[level - 1];
} catch (Exception e) {
return hpBase;
}
}
public float getBaseAttack(int level) {
val atkBase = avatarData.getAttackBase();
try {
return atkBase * this.attackGrowthCurve[level - 1];
} catch (Exception e) {
return atkBase;
}
}
public float getBaseDefense(int level) {
val defenseBase = avatarData.getAttackBase();
try {
return defenseBase * this.defenseGrowthCurve[level - 1];
} catch (Exception e) {
return defenseBase;
}
}
public float getBaseCritical() {
return avatarData.getCritical();
}
public float getBaseCriticalHurt() {
return avatarData.getCriticalHurt();
}
public int getInitialWeapon(){
return avatarData.getInitialWeapon();
}
@NonNull public WeaponType getWeaponType(){
return avatarData.getWeaponTypeNonNull();
}
}

View File

@ -1,41 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.activity.condition.ActivityConditions;
import emu.grasscutter.game.quest.enums.LogicType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import java.util.List;
@ResourceType(name = "NewActivityCondExcelConfigData.json")
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityCondExcelConfigData extends GameResource {
int condId;
LogicType condComb;
List<ActivityConfigCondition> cond;
public static class ActivityConfigCondition {
@Getter
private ActivityConditions type;
@Getter
private List<Integer> param;
public int[] paramArray() {
return param.stream().mapToInt(Integer::intValue).toArray();
}
}
@Override
public int getId() {
return condId;
}
@Override
public void onLoad() {
cond.removeIf(c -> c.type == null);
}
}

View File

@ -1,34 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import java.util.List;
import java.util.Objects;
@ResourceType(name = "NewActivityExcelConfigData.json", loadPriority = ResourceType.LoadPriority.LOW)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityData extends GameResource {
int activityId;
String activityType;
List<Integer> condGroupId;
List<Integer> watcherId;
List<ActivityWatcherData> watcherDataList;
@Override
public int getId() {
return this.activityId;
}
@Override
public void onLoad() {
this.watcherDataList = watcherId.stream().map(item -> GameData.getActivityWatcherDataMap().get(item.intValue()))
.filter(Objects::nonNull)
.toList();
}
}

View File

@ -1,30 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.shop.ShopType;
import lombok.Getter;
import java.util.List;
@ResourceType(name = "ActivityShopOverallExcelConfigData.json")
public class ActivityShopData extends GameResource {
@Getter
private int scheduleId;
@Getter
private ShopType shopType;
@Getter
private List<Integer> sheetList;
@Override
public int getId() {
return getShopTypeId();
}
public int getShopTypeId() {
if (this.shopType == null)
this.shopType = ShopType.SHOP_TYPE_NONE;
return shopType.shopTypeId;
}
}

View File

@ -10,6 +10,7 @@ import lombok.experimental.FieldDefaults;
import java.util.List;
@Deprecated
@ResourceType(name = "NewActivityWatcherConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
@Getter
@FieldDefaults(level = AccessLevel.PRIVATE)

View File

@ -1,37 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarCostumeExcelConfigData.json")
public class AvatarCostumeData extends GameResource {
@SerializedName(value = "skinId", alternate = "costumeId")
private int skinId;
private int itemId;
private int characterId;
private int quality;
@Override
public int getId() {
return this.skinId;
}
public int getItemId() {
return this.itemId;
}
public int getCharacterId() {
return characterId;
}
public int getQuality() {
return quality;
}
@Override
public void onLoad() {
GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);
}
}

View File

@ -1,36 +0,0 @@
package emu.grasscutter.data.excels;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.CurveInfo;
@ResourceType(name = "AvatarCurveExcelConfigData.json")
public class AvatarCurveData extends GameResource {
private int level;
private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public Map<String, Float> getCurveInfos() {
return curveInfoMap;
}
@Override
public void onLoad() {
this.curveInfoMap = new HashMap<>();
Stream.of(this.curveInfos).forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue()));
}
}

View File

@ -1,176 +0,0 @@
package emu.grasscutter.data.excels;
import java.util.ArrayList;
import java.util.List;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.binout.AbilityEmbryoEntry;
import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.WeaponType;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter;
@ResourceType(name = "AvatarExcelConfigData.json", loadPriority = LoadPriority.LOW)
public class AvatarData extends GameResource {
private String iconName;
@Getter private String bodyType;
@Getter private String qualityType;
@Getter private int chargeEfficiency;
@Getter private int initialWeapon;
@Getter private WeaponType weaponType;
@Getter private String imageName;
@Getter private int avatarPromoteId;
@Getter private String cutsceneShow;
@Getter private int skillDepotId;
@Getter private int staminaRecoverSpeed;
@Getter private List<Integer> candSkillDepotIds;
@Getter private String avatarIdentityType;
@Getter private List<Integer> avatarPromoteRewardLevelList;
@Getter private List<Integer> avatarPromoteRewardIdList;
@Getter private long nameTextMapHash;
private float hpBase;
private float attackBase;
private float defenseBase;
private float critical;
private float criticalHurt;
private List<PropGrowCurve> propGrowCurves;
@Getter(onMethod = @__(@Override))
private int id;
// Transient
@Getter private String name;
private Int2ObjectMap<String> growthCurveMap;
private float[] hpGrowthCurve;
private float[] attackGrowthCurve;
private float[] defenseGrowthCurve;
@Getter private AvatarSkillDepotData skillDepot;
@Getter private IntList abilities;
@Getter private List<String> abilitieNames = new ArrayList<>();
@Getter private List<Integer> fetters;
@Getter private int nameCardRewardId;
@Getter private int nameCardId;
public float getBaseHp(int level) {
try {
return this.hpBase * this.hpGrowthCurve[level - 1];
} catch (Exception e) {
return this.hpBase;
}
}
public float getBaseAttack(int level) {
try {
return this.attackBase * this.attackGrowthCurve[level - 1];
} catch (Exception e) {
return this.attackBase;
}
}
public float getBaseDefense(int level) {
try {
return this.defenseBase * this.defenseGrowthCurve[level - 1];
} catch (Exception e) {
return this.defenseBase;
}
}
public float getBaseCritical() {
return this.critical;
}
public float getBaseCriticalHurt() {
return this.criticalHurt;
}
public float getGrowthCurveById(int level, FightProperty prop) {
String growCurve = this.growthCurveMap.get(prop.getId());
if (growCurve == null) {
return 1f;
}
AvatarCurveData curveData = GameData.getAvatarCurveDataMap().get(level);
if (curveData == null) {
return 1f;
}
return curveData.getCurveInfos().getOrDefault(growCurve, 1f);
}
@Override
public void onLoad() {
this.skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.skillDepotId);
// Get fetters from GameData
this.fetters = GameData.getFetterDataEntries().get(this.id);
if (GameData.getFetterCharacterCardDataMap().get(this.id) != null) {
this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.id).getRewardId();
}
if (GameData.getRewardDataMap().get(this.nameCardRewardId) != null) {
this.nameCardId = GameData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId();
}
int size = GameData.getAvatarCurveDataMap().size();
this.hpGrowthCurve = new float[size];
this.attackGrowthCurve = new float[size];
this.defenseGrowthCurve = new float[size];
for (AvatarCurveData curveData : GameData.getAvatarCurveDataMap().values()) {
int level = curveData.getLevel() - 1;
for (PropGrowCurve growCurve : this.propGrowCurves) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
switch (prop) {
case FIGHT_PROP_BASE_HP:
this.hpGrowthCurve[level] = curveData.getCurveInfos().get(growCurve.getGrowCurve());
break;
case FIGHT_PROP_BASE_ATTACK:
this.attackGrowthCurve[level] = curveData.getCurveInfos().get(growCurve.getGrowCurve());
break;
case FIGHT_PROP_BASE_DEFENSE:
this.defenseGrowthCurve[level] = curveData.getCurveInfos().get(growCurve.getGrowCurve());
break;
default:
break;
}
}
}
/*
for (PropGrowCurve growCurve : this.PropGrowCurves) {
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
this.growthCurveMap.put(prop.getId(), growCurve.getGrowCurve());
}
*/
rebuildAbilityEmbryo();
}
public void rebuildAbilityEmbryo(){// rebuild ability for different scene
// Cache abilities
String[] split = this.iconName.split("_");
if (split.length > 0) {
this.name = split[split.length - 1];
AbilityEmbryoEntry info = GameData.getAbilityEmbryoInfo().get(this.name);
if (info != null) {
this.abilities = new IntArrayList(info.getAbilities().length);
for (String ability : info.getAbilities()) {
this.abilities.add(Utils.abilityHash(ability));
abilitieNames.add(ability);
}
}
}
}
}

View File

@ -1,23 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFettersLevelExcelConfigData.json")
public class AvatarFetterLevelData extends GameResource {
private int fetterLevel;
private int needExp;
@Override
public int getId() {
return this.fetterLevel;
}
public int getLevel() {
return fetterLevel;
}
public int getExp() {
return needExp;
}
}

View File

@ -1,24 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarFlycloakExcelConfigData.json")
public class AvatarFlycloakData extends GameResource {
private int flycloakId;
private long nameTextMapHash;
@Override
public int getId() {
return this.flycloakId;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
@Override
public void onLoad() {
}
}

View File

@ -1,23 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "AvatarLevelExcelConfigData.json")
public class AvatarLevelData extends GameResource {
private int level;
private int exp;
@Override
public int getId() {
return this.level;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
}

View File

@ -1,25 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
import java.util.List;
@ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH)
@Getter
@Setter
@FieldDefaults(level = AccessLevel.PRIVATE)
public class CityData extends GameResource {
int cityId;
int sceneId;
List<Integer> areaIdVec;
@Override
public int getId() {
return this.cityId;
}
}

View File

@ -1,70 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.enums.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import java.util.List;
import java.util.Optional;
@EqualsAndHashCode(callSuper = false)
@ResourceType(name = "DungeonExcelConfigData.json")
@ToString
@Data
public class DungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int serialId;
private int sceneId;
private int showLevel;
private int levelRevise;
private DungeonType type;
private DungeonSubType subType;
private DungeonPlayType playType;
private DungeonInvolveType involveType;
private int limitLevel;
private int passCond;
private int reviveMaxCount;
private int settleCountdownTime;
private int failSettleCountdownTime;
private int quitSettleCountdownTime;
private List<SettleShowType> settleShows;
@SerializedName(value = "passRewardPreviewID", alternate = {"passRewardPreviewId"})
private int passRewardPreviewId;
private int passJumpDungeon;
private int statueCostID;
private int statueCostCount;
private String stateType;
// not part of DungeonExcelConfigData
private RewardPreviewData rewardPreviewData;
public DungeonType getType() {
return Optional.ofNullable(this.type).orElse(DungeonType.DUNGEON_NONE);
}
public DungeonSubType getSubType() {
return Optional.ofNullable(this.subType).orElse(DungeonSubType.DUNGEON_SUB_NONE);
}
public DungeonPlayType getPlayType() {
return Optional.ofNullable(this.playType).orElse(DungeonPlayType.DUNGEON_PLAY_TYPE_NONE);
}
public DungeonInvolveType getInvolveType() {
return Optional.ofNullable(this.involveType).orElse(DungeonInvolveType.INVOLVE_NONE);
}
@Override
public void onLoad() {
Optional.ofNullable(GameData.getRewardPreviewDataMap().get(this.passRewardPreviewId))
.ifPresent(d -> this.rewardPreviewData = d);
}
}

View File

@ -1,43 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.quest.enums.LogicType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import java.util.List;
@ResourceType(name = "DungeonPassExcelConfigData.json")
@AllArgsConstructor
public class DungeonPassConfigData extends GameResource {
public static final DungeonPassConfigData EMPTY =
new DungeonPassConfigData(0, LogicType.LOGIC_OR, List.of(
new DungeonPassCondition(DungeonPassConditionType.DUNGEON_COND_NONE, new int[]{0})
));
static {
GameData.getDungeonPassConfigDataMap().put(EMPTY.getId(), EMPTY);
}
@Getter private int id;
@Getter private LogicType logicType;
@Getter private List<DungeonPassCondition> conds;
@Data @AllArgsConstructor
public static class DungeonPassCondition{
@Getter private DungeonPassConditionType condType;
@Getter int[] param;
}
@Override
public void onLoad() {
super.onLoad();
conds = conds.stream().filter(condition -> condition.getCondType()!=null).toList();
}
}

View File

@ -6,7 +6,9 @@ import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import org.anime_game_servers.game_data_models.gi.data.city.CityData;
import javax.annotation.Nullable;
import java.util.List;
@ResourceType(name = "InvestigationMonsterConfigData.json", loadPriority = ResourceType.LoadPriority.LOW)
@ -22,10 +24,8 @@ public class InvestigationMonsterData extends GameResource {
String mapMarkCreateType;
String monsterCategory;
CityData cityData;
@Override
public void onLoad() {
this.cityData = GameData.getCityDataMap().get(cityId);
@Nullable
public CityData getCityData(){
return GameData.getCityDataMap().get(cityId);
}
}

View File

@ -1,27 +0,0 @@
package emu.grasscutter.data.excels;
import java.util.List;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
@ResourceType(name = "RewardExcelConfigData.json")
public class RewardData extends GameResource {
public int rewardId;
public List<ItemParamData> rewardItemList;
@Override
public int getId() {
return rewardId;
}
public List<ItemParamData> getRewardItemList() {
return rewardItemList;
}
@Override
public void onLoad() {
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();
}
}

View File

@ -1,24 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType;
import lombok.Getter;
import java.util.List;
import lombok.Getter;
@ResourceType(name = "SceneExcelConfigData.json")
@Getter
public class SceneData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
@SerializedName("type")
private SceneType sceneType;
private String scriptData;
@Getter private String levelEntityConfig;
@Getter private List<Integer> specifiedAvatarList;
}

View File

@ -1,17 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
import java.util.List;
@ResourceType(name = "SceneTagConfigData.json")
@Getter
public class SceneTagData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private String sceneTagName;
private int sceneId;
private boolean isDefaultValid;
private boolean isSkipLoading;
}

View File

@ -1,110 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.shop.ShopInfo;
import java.util.List;
@ResourceType(name = "ShopGoodsExcelConfigData.json")
public class ShopGoodsData extends GameResource {
private int goodsId;
private int shopType;
private int itemId;
private int itemCount;
private int costScoin;
private int costHcoin;
private int costMcoin;
private List<ItemParamData> costItems;
private int minPlayerLevel;
private int maxPlayerLevel;
private int buyLimit;
@SerializedName(value="subTabId", alternate={"secondarySheetId"})
private int subTabId;
private String refreshType;
private transient ShopInfo.ShopRefreshType refreshTypeEnum;
private int refreshParam;
@Override
public void onLoad() {
if (this.refreshType == null)
this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE;
else {
this.refreshTypeEnum = switch (this.refreshType) {
case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY;
case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY;
case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY;
default -> ShopInfo.ShopRefreshType.NONE;
};
}
}
@Override
public int getId() {
return getGoodsId();
}
public int getGoodsId() {
return goodsId;
}
public int getShopType() {
return shopType;
}
public int getItemId() {
return itemId;
}
public int getItemCount() {
return itemCount;
}
public int getCostScoin() {
return costScoin;
}
public int getCostHcoin() {
return costHcoin;
}
public int getCostMcoin() {
return costMcoin;
}
public List<ItemParamData> getCostItems() {
return costItems;
}
public int getMinPlayerLevel() {
return minPlayerLevel;
}
public int getMaxPlayerLevel() {
return maxPlayerLevel;
}
public int getBuyLimit() {
return buyLimit;
}
public int getSubTabId() {
return subTabId;
}
public ShopInfo.ShopRefreshType getRefreshType() {
return refreshTypeEnum;
}
public int getRefreshParam() {
return refreshParam;
}
}

View File

@ -1,21 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "StatuePromoteExcelConfigData.json")
public class StatuePromoteData extends GameResource {
@Getter @Setter private int level;
@Getter @Setter private int cityId;
@Getter @Setter private ItemParamData[] costItems;
@Getter @Setter private int[] rewardIdList;
@Getter @Setter private int stamina;
@Override
public int getId() {
return (cityId << 8) + level;
}
}

View File

@ -1,48 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.val;
import java.util.List;
import java.util.stream.IntStream;
@ResourceType(name = "TowerRewardExcelConfigData.json")
@Data
@EqualsAndHashCode(callSuper = false)
@ToString
@SuppressWarnings("SpellCheckingInspection")
public class TowerRewardData extends GameResource {
@SerializedName(value = "levelIndex", alternate = {"IEPBHNILIDB"})
private int levelIndex;
@SerializedName(value = "floorIndex", alternate = {"DGKHCGBPFDM"})
private int floorIndex;
@SerializedName(value = "threeStarsReward", alternate = {"BCOFGJBABGD"})
private int threeStarsReward;
@SerializedName(value = "sixStarsReward", alternate = {"CAEOHANFNAP"})
private int sixStarsReward;
@SerializedName(value = "nineStarsReward", alternate = {"DGJDIBJKGMI"})
private int nineStarsReward;
@SerializedName(value = "chamberBountyRewards", alternate = {"MHDLDDFOIJD"})
private List<Integer> chamberBountyRewards;
@Override
public int getId() {
return (this.levelIndex << 4) + this.floorIndex;
}
public List<Integer> getStarRewardsByStarCount(int starCount) {
val fullStarRewards = List.of(this.threeStarsReward, this.sixStarsReward, this.nineStarsReward);
return IntStream.rangeClosed(0, starCount).map(i -> (int) Math.floor((float) i / 3) - 1).filter(i -> i >= 0)
.distinct().mapToObj(fullStarRewards::get).toList();
}
public List<Integer> getFirstPassRewardByStarCount(int starCount) {
return IntStream.rangeClosed(0, starCount).map(i -> (int) Math.floor((float) i / 3) - 1).filter(i -> i >= 0)
.distinct().mapToObj(this.chamberBountyRewards::get).toList();
}
}

View File

@ -1,20 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "TriggerExcelConfigData.json") @Getter
public class TriggerExcelConfigData extends GameResource {
@Getter private int id;
private int sceneId;
private int groupId;
private String triggerName;
@Override
public void onLoad() {
super.onLoad();
GameData.getTriggerDataByNameMap().put(groupId+triggerName, this);
}
}

View File

@ -1,25 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.ElementType;
import lombok.Getter;
@ResourceType(name = "WorldAreaConfigData.json")
public class WorldAreaData extends GameResource {
private int ID;
@Getter @SerializedName("AreaID1")
private int areaID1;
@Getter @SerializedName("AreaID2")
private int areaID2;
@Getter @SerializedName("SceneID")
private int sceneID;
@Getter
private ElementType elementType;
@Override
public int getId() {
return (this.areaID2 << 16) + this.areaID1;
}
}

View File

@ -1,24 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
@ResourceType(name = "WorldLevelExcelConfigData.json")
public class WorldLevelData extends GameResource {
private int level;
private int monsterLevel;
@Override
public int getId() {
return this.level;
}
public int getMonsterLevel() {
return monsterLevel;
}
@Override
public void onLoad() {
}
}

View File

@ -1,11 +0,0 @@
package emu.grasscutter.data.server;
import lombok.Data;
import java.util.List;
@Data
public class ActivityCondGroup {
int condGroupId;
List<Integer> condIds;
}

View File

@ -0,0 +1,73 @@
package emu.grasscutter.game;
import emu.grasscutter.Grasscutter;
import org.anime_game_servers.game_data_models.gi.data.general.LogicType;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.function.BooleanSupplier;
public class LogicTypeUtils {
public static boolean calculate(LogicType logicType, int[] progress) {
if (progress.length == 0) {
return true;
}
if (logicType == null) {
return progress[0] == 1;
}
switch (logicType) {
case LOGIC_AND -> {
return Arrays.stream(progress).allMatch(i -> i == 1);
}
case LOGIC_OR -> {
return Arrays.stream(progress).anyMatch(i -> i == 1);
}
case LOGIC_NOT -> {
return Arrays.stream(progress).noneMatch(i -> i == 1);
}
case LOGIC_A_AND_ETCOR -> {
return progress[0] == 1 && Arrays.stream(progress).skip(1).anyMatch(i -> i == 1);
}
case LOGIC_A_AND_B_AND_ETCOR -> {
return progress[0] == 1 && progress[1] == 1 && Arrays.stream(progress).skip(2).anyMatch(i -> i == 1);
}
case LOGIC_A_OR_ETCAND -> {
return progress[0] == 1 || Arrays.stream(progress).skip(1).allMatch(i -> i == 1);
}
case LOGIC_A_OR_B_OR_ETCAND -> {
return progress[0] == 1 || progress[1] == 1 || Arrays.stream(progress).skip(2).allMatch(i -> i == 1);
}
case LOGIC_A_AND_B_OR_ETCAND -> {
return progress[0] == 1 && progress[1] == 1 || Arrays.stream(progress).skip(2).allMatch(i -> i == 1);
}
default -> {
return Arrays.stream(progress).anyMatch(i -> i == 1);
}
}
}
/**
* Apply logic type to all predicates
*
* @param logicType type of logic that should be applied to predicates
* @param predicates list of predicates for which logicType will be applied
* @return result of applying logicType to predicates
*/
public static boolean calculate(@NotNull LogicType logicType, List<BooleanSupplier> predicates) {
switch (logicType) {
case LOGIC_AND -> {
return predicates.stream().allMatch(BooleanSupplier::getAsBoolean);
}
case LOGIC_OR -> {
return predicates.stream().anyMatch(BooleanSupplier::getAsBoolean);
}
default -> {
Grasscutter.getLogger().error("Unimplemented logic operation was called");
return false;
}
}
}
}

View File

@ -8,9 +8,9 @@ import emu.grasscutter.game.ability.Ability;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.utils.Position;
import org.anime_game_servers.multi_proto.gi.messages.ability.action.AbilityActionGenerateElemBall;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneType;
@AbilityAction(AbilityModifierAction.Type.GenerateElemBall)
public class ActionGenerateElemBall extends AbilityActionHandler {
@ -34,7 +34,7 @@ public class ActionGenerateElemBall extends AbilityActionHandler {
return true;
}
} else if(action.dropType == DropType.BigWorldOnly) {
if(owner.getScene().getSceneData().getSceneType() != SceneType.SCENE_WORLD) {
if(owner.getScene().getSceneData().getType() != SceneType.SCENE_WORLD) {
logger.warn("This level config only allows element balls on big world");
return true;
}

View File

@ -2,11 +2,8 @@ package emu.grasscutter.game.activity;
import com.esotericsoftware.reflectasm.ConstructorAccess;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.ActivityData;
import emu.grasscutter.data.server.ActivityCondGroup;
import emu.grasscutter.game.activity.condition.ActivityConditionExecutor;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.utils.DateHelper;
import lombok.AccessLevel;
@ -15,6 +12,9 @@ import lombok.Setter;
import lombok.experimental.FieldDefaults;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondGroupData;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityData;
import org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType;
import java.util.*;
import java.util.stream.Collectors;
@ -30,6 +30,7 @@ public abstract class ActivityHandler {
@Getter ActivityData activityData;
Map<WatcherTriggerType, List<ActivityWatcher>> watchersMap = new HashMap<>();
abstract public void onProtoBuild(PlayerActivityData playerActivityData, ActivityInfo activityInfo);
abstract public void onInitPlayerActivityData(PlayerActivityData playerActivityData);
@ -37,8 +38,14 @@ public abstract class ActivityHandler {
activityData = GameData.getActivityDataMap().get(activityConfigItem.getActivityId());
// add watcher to map by id
activityData.getWatcherDataList().forEach(watcherData -> {
var watcherType = activityWatcherTypeMap.get(watcherData.getTriggerConfig().getWatcherTriggerType());
activityData.getWatcherIds().forEach(watcherId -> {
val watcherData = GameData.getActivityWatcherDataMap().get(watcherId.intValue());
if(watcherData == null || watcherData.getTriggerConfig() == null || watcherData.getTriggerConfig().getTriggerType() == null){
// todo log
return;
}
val triggerType = watcherData.getTriggerConfig().getTriggerType();
var watcherType = activityWatcherTypeMap.get(triggerType);
ActivityWatcher watcher;
if(watcherType != null){
watcher = (ActivityWatcher) watcherType.newInstance();
@ -49,8 +56,8 @@ public abstract class ActivityHandler {
watcher.setWatcherId(watcherData.getId());
watcher.setActivityHandler(this);
watcher.setActivityWatcherData(watcherData);
watchersMap.computeIfAbsent(watcherData.getTriggerConfig().getWatcherTriggerType(), k -> new ArrayList<>());
watchersMap.get(watcherData.getTriggerConfig().getWatcherTriggerType()).add(watcher);
watchersMap.computeIfAbsent(triggerType, k -> new ArrayList<>());
watchersMap.get(triggerType).add(watcher);
});
}
@ -59,7 +66,7 @@ public abstract class ActivityHandler {
return;
}
val questManager = player.getQuestManager();
activityData.getCondGroupId().forEach(condGroupId -> {
activityData.getCondGroupIds().forEach(condGroupId -> {
val condGroup = GameData.getActivityCondGroupMap().get((int)condGroupId);
if(condGroup != null)
condGroup.getCondIds().forEach(condID -> questManager.queueEvent(QuestCond.QUEST_COND_ACTIVITY_COND, condID));
@ -70,9 +77,9 @@ public abstract class ActivityHandler {
if(activityData == null){
return new ArrayList<>();
}
return activityData.getCondGroupId().stream().map(condGroupId -> GameData.getActivityCondGroupMap().get((int)condGroupId))
return activityData.getCondGroupIds().stream().map(condGroupId -> GameData.getActivityCondGroupMap().get((int)condGroupId))
.filter(Objects::nonNull)
.map(ActivityCondGroup::getCondIds)
.map(ActivityCondGroupData::getCondIds)
.flatMap(Collection::stream)
.toList();
}

View File

@ -7,17 +7,16 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.game.activity.condition.*;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.server.packet.send.PacketActivityScheduleInfoNotify;
import lombok.Getter;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.reflections.Reflections;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType;
@Getter
public class ActivityManager extends BasePlayerManager {
@ -37,7 +36,7 @@ public class ActivityManager extends BasePlayerManager {
// scan activity type handler & watcher type
var activityHandlerTypeMap = new HashMap<ActivityType, ConstructorAccess<?>>();
var activityWatcherTypeMap = new HashMap<WatcherTriggerType, ConstructorAccess<?>>();
var reflections = new Reflections(ActivityManager.class.getPackage().getName());
var reflections = Grasscutter.reflector;
reflections.getSubTypesOf(ActivityHandler.class).forEach(item -> {
var typeName = item.getAnnotation(GameActivity.class);
@ -51,12 +50,14 @@ public class ActivityManager extends BasePlayerManager {
try {
DataLoader.loadList("ActivityConfig.json", ActivityConfigItem.class).forEach(item -> {
item.onLoad();
var activityData = GameData.getActivityDataMap().get(item.getActivityId());
val activityData = GameData.getActivityDataMap().get(item.getActivityId());
if (activityData == null) {
Grasscutter.getLogger().warn("activity {} not exist.", item.getActivityId());
return;
}
var activityHandlerType = activityHandlerTypeMap.get(ActivityType.getTypeByName(activityData.getActivityType()));
// todo handle exception
val activityHandlerType = activityHandlerTypeMap.get(activityData.getActivityType());
ActivityHandler activityHandler;
if (activityHandlerType != null) {

View File

@ -1,10 +1,10 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.data.excels.ActivityWatcherData;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
import org.anime_game_servers.game_data_models.gi.data.watcher.ActivityWatcherData;
@Getter
@Setter

View File

@ -1,6 +1,6 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.WatcherTriggerType;
import org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -1,9 +1,9 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.ActivityType;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
@GameActivity(ActivityType.NONE)
@GameActivity(ActivityType.NEW_ACTIVITY_GENERAL)
public class DefaultActivityHandler extends ActivityHandler{
@Override
public void onProtoBuild(PlayerActivityData playerActivityData, ActivityInfo activityInfo) {

View File

@ -1,8 +1,8 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.WatcherTriggerType;
import static org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType.TRIGGER_NONE;
@ActivityWatcherType(WatcherTriggerType.TRIGGER_NONE)
@ActivityWatcherType(TRIGGER_NONE)
public class DefaultWatcher extends ActivityWatcher{
@Override
protected boolean isMeet(String... param) {

View File

@ -1,6 +1,6 @@
package emu.grasscutter.game.activity;
import emu.grasscutter.game.props.ActivityType;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;

View File

@ -4,19 +4,17 @@ import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Transient;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ActivityWatcherData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.server.packet.send.PacketActivityUpdateWatcherNotify;
import emu.grasscutter.utils.JsonUtils;
import lombok.*;
import lombok.experimental.FieldDefaults;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityWatcherInfo;
import org.anime_game_servers.game_data_models.gi.data.watcher.ActivityWatcherData;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -77,21 +75,16 @@ public class PlayerActivityData {
return;
}
var reward = Optional.of(watcher)
val reward = Optional.of(watcher)
.map(WatcherInfo::getMetadata)
.map(ActivityWatcherData::getRewardID)
.map(ActivityWatcherData::getRewardId)
.map(id -> GameData.getRewardDataMap().get(id.intValue()));
if (reward.isEmpty()) {
return;
}
List<GameItem> rewards = new ArrayList<>();
for (ItemParamData param : reward.get().getRewardItemList()) {
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
}
player.getInventory().addItems(rewards, ActionReason.ActivityWatcher);
player.getInventory().addRewardData(reward.get(), ActionReason.ActivityWatcher);
watcher.setTakenReward(true);
save();
}

View File

@ -3,12 +3,12 @@ package emu.grasscutter.game.activity.aster;
import emu.grasscutter.game.activity.ActivityHandler;
import emu.grasscutter.game.activity.GameActivity;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.server.packet.send.*;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.anime_game_servers.multi_proto.gi.messages.activity.aster.*;
import org.anime_game_servers.multi_proto.gi.messages.general.Vector;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import java.util.List;
import java.util.Map;

View File

@ -1,11 +1,13 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondData;
import java.util.List;
/**
* Base handler for all activity conditions that are listed in NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData})
* Base handler for all activity conditions that are listed in NewActivityCondExcelConfigData.json ({@link ActivityCondData})
*/
public abstract class ActivityConditionBaseHandler {
@ -17,5 +19,5 @@ public abstract class ActivityConditionBaseHandler {
* @param params params for handler
* @return result of condition calculation
*/
public abstract boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params);
public abstract boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params);
}

View File

@ -1,17 +1,18 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondData;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}). To use it you should mark
* This annotation marks condition types for NewActivityCondExcelConfigData.json ({@link ActivityCondData}). To use it you should mark
* class that extends ActivityConditionBaseHandler, and it will be automatically picked during activity manager initiation.
*
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityCondition {
ActivityConditions value();
public @interface ActivityConditionHandler {
ActivityCondition value();
}

View File

@ -1,16 +1,16 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
import org.reflections.Reflections;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondData;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition;
import java.util.AbstractMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Class that used for scanning classpath, picking up all activity conditions (for NewActivityCondExcelConfigData.json {@link ActivityCondExcelConfigData})
* and saving them to map. Check for more info {@link ActivityCondition}
* Class that used for scanning classpath, picking up all activity conditions (for NewActivityCondExcelConfigData.json {@link ActivityCondData})
* and saving them to map. Check for more info {@link ActivityConditionHandler}
*/
public class AllActivityConditionBuilder {
@ -19,20 +19,19 @@ public class AllActivityConditionBuilder {
*
* @return map containing all condition handlers for NewActivityCondExcelConfigData.json
*/
static public Map<ActivityConditions, ActivityConditionBaseHandler> buildActivityConditions() {
static public Map<ActivityCondition, ActivityConditionBaseHandler> buildActivityConditions() {
return new AllActivityConditionBuilder().initActivityConditions();
}
private Map<ActivityConditions, ActivityConditionBaseHandler> initActivityConditions() {
Reflections reflector = Grasscutter.reflector;
return reflector.getTypesAnnotatedWith(ActivityCondition.class).stream()
private Map<ActivityCondition, ActivityConditionBaseHandler> initActivityConditions() {
return Grasscutter.reflector.getTypesAnnotatedWith(ActivityConditionHandler.class).stream()
.map(this::newInstance)
.map(h -> new AbstractMap.SimpleEntry<>(extractActionType(h), h))
.collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
}
private ActivityConditions extractActionType(ActivityConditionBaseHandler e) {
ActivityCondition condition = e.getClass().getAnnotation(ActivityCondition.class);
private ActivityCondition extractActionType(ActivityConditionBaseHandler e) {
ActivityConditionHandler condition = e.getClass().getAnnotation(ActivityConditionHandler.class);
if (condition == null) {
Grasscutter.getLogger().error("Failed to read command type for class {}", e.getClass().getName());
return null;

View File

@ -1,12 +1,15 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
import emu.grasscutter.game.LogicTypeUtils;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.all.UnknownActivityConditionHandler;
import emu.grasscutter.game.quest.enums.LogicType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondData;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition;
import org.anime_game_servers.game_data_models.gi.data.general.LogicType;
import java.util.List;
import java.util.Map;
@ -16,17 +19,17 @@ import java.util.stream.Collectors;
public class BasicActivityConditionExecutor implements ActivityConditionExecutor {
private final Map<Integer, ActivityConfigItem> activityConfigItemMap;
private final Int2ObjectMap<ActivityCondExcelConfigData> activityConditions;
private final Int2ObjectMap<ActivityCondData> activityConditions;
private final Int2ObjectMap<PlayerActivityData> playerActivityDataByActivityCondId;
private final Map<ActivityConditions, ActivityConditionBaseHandler> activityConditionsHandlers;
private final Map<ActivityCondition, ActivityConditionBaseHandler> activityConditionsHandlers;
private static final UnknownActivityConditionHandler UNKNOWN_CONDITION_HANDLER = new UnknownActivityConditionHandler();
public BasicActivityConditionExecutor(Map<Integer, ActivityConfigItem> activityConfigItemMap,
Int2ObjectMap<ActivityCondExcelConfigData> activityConditions,
Int2ObjectMap<ActivityCondData> activityConditions,
Int2ObjectMap<PlayerActivityData> playerActivityDataByActivityCondId,
Map<ActivityConditions, ActivityConditionBaseHandler> activityConditionsHandlers) {
Map<ActivityCondition, ActivityConditionBaseHandler> activityConditionsHandlers) {
this.activityConfigItemMap = activityConfigItemMap;
this.activityConditions = activityConditions;
this.playerActivityDataByActivityCondId = playerActivityDataByActivityCondId;
@ -42,14 +45,14 @@ public class BasicActivityConditionExecutor implements ActivityConditionExecutor
@Override
public boolean meetsCondition(int activityCondId) {
ActivityCondExcelConfigData condData = activityConditions.get(activityCondId);
val condData = activityConditions.get(activityCondId);
if (condData == null) {
Grasscutter.getLogger().error("Could not find condition for activity with id = {}", activityCondId);
return false;
}
LogicType condComb = condData.getCondComb();
var condComb = condData.getCondComb();
if (condComb == null) {
condComb = LogicType.LOGIC_AND;
}
@ -59,13 +62,16 @@ public class BasicActivityConditionExecutor implements ActivityConditionExecutor
return false;
}
ActivityConfigItem activityConfig = activityConfigItemMap.get(activity.getActivityId());
if(condData.getCond() == null){
return false;
}
List<BooleanSupplier> predicates = condData.getCond()
.stream()
.map(c -> (BooleanSupplier) () ->
activityConditionsHandlers
.getOrDefault(c.getType(), UNKNOWN_CONDITION_HANDLER).execute(activity, activityConfig, c.paramArray()))
.getOrDefault(c.getType(), UNKNOWN_CONDITION_HANDLER).execute(activity, activityConfig, c.getParam()))
.collect(Collectors.toList());
return LogicType.calculate(condComb, predicates);
return LogicTypeUtils.calculate(condComb, predicates);
}
}

View File

@ -1,11 +1,11 @@
package emu.grasscutter.game.activity.condition;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.ActivityCondExcelConfigData;
import emu.grasscutter.game.activity.PlayerActivityData;
import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap.BasicEntry;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondData;
import java.util.Map;
@ -17,12 +17,12 @@ public class PlayerActivityDataMappingBuilder {
private final Map<Integer, PlayerActivityData> playerActivityDataMap;
private final Int2ObjectMap<ActivityCondExcelConfigData> activityCondMap;
private final Int2ObjectMap<ActivityCondData> activityCondMap;
/**
* Build mapping for PlayerActivityData.
*
* @return mapping for activity data. Key is condId from NewActivityCondExcelConfigData.json ({@link ActivityCondExcelConfigData}) resource,
* @return mapping for activity data. Key is condId from NewActivityCondExcelConfigData.json ({@link ActivityCondData}) resource,
* value is {@link PlayerActivityData} class for related activity.
*/
public static Int2ObjectMap<PlayerActivityData> buildPlayerActivityDataByActivityCondId(Map<Integer, PlayerActivityData> activities) {
@ -53,7 +53,7 @@ public class PlayerActivityDataMappingBuilder {
/**
* Detect activity data id by cond id. Cond id comes from condId field from NewActivityCondExcelConfigData.json.
* See {@link ActivityCondExcelConfigData} for condId.
* See {@link ActivityCondData} for condId.
* <p>
* Generally, there are 3 cases:
* <ol>

View File

@ -2,15 +2,18 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_LESS;
import java.util.List;
@ActivityCondition(NEW_ACTIVITY_COND_DAYS_LESS)
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_DAYS_LESS;
@ActivityConditionHandler(NEW_ACTIVITY_COND_DAYS_LESS)
public class DayLess extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
return true; //TODO implement this and add possibility to always return true
}
}

View File

@ -2,20 +2,20 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL;
import java.util.Date;
import java.util.List;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL;
@ActivityCondition(NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL)
@ActivityConditionHandler(NEW_ACTIVITY_COND_DAYS_GREAT_EQUAL)
public class DaysGreatEqual extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
Date activityBeginTime = activityConfig.getBeginTime();
long timeDiff = System.currentTimeMillis() - activityBeginTime.getTime();
int days = (int) (timeDiff / (1000 * 60 * 60 * 24L));
return days + 1 >= params[0];
return days + 1 >= params.get(0);
}
}

View File

@ -2,16 +2,19 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import emu.grasscutter.game.activity.condition.ActivityConditions;
import lombok.val;
@ActivityCondition(ActivityConditions.NEW_ACTIVITY_COND_FINISH_WATCHER)
import java.util.List;
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_FINISH_WATCHER;
@ActivityConditionHandler(NEW_ACTIVITY_COND_FINISH_WATCHER)
public class FinishWatcher extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
val watcherMap = activityData.getWatcherInfoMap();
for (int param : params) {
val watcher = watcherMap.get(param);

View File

@ -2,21 +2,23 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_NOT_FINISH_TALK;
import java.util.List;
@ActivityCondition(NEW_ACTIVITY_COND_NOT_FINISH_TALK)
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_NOT_FINISH_TALK;
@ActivityConditionHandler(NEW_ACTIVITY_COND_NOT_FINISH_TALK)
public class NotFinishTalk extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
return activityData
.getPlayer()
.getQuestManager()
.getMainQuests()
.int2ObjectEntrySet()
.stream()
.noneMatch(q -> q.getValue().getTalks().get(params[0]) != null); //FIXME taken from ContentCompleteTalk
.noneMatch(q -> q.getValue().getTalks().get(params.get(0)) != null); //FIXME taken from ContentCompleteTalk
}
}

View File

@ -2,16 +2,18 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL;
import java.util.List;
@ActivityCondition(NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL)
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL;
@ActivityConditionHandler(NEW_ACTIVITY_COND_PLAYER_LEVEL_GREAT_EQUAL)
public class PlayerLevelGreatEqualActivityActivityCondition extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
return activityData.getPlayer().getLevel() >= params[0];
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
return activityData.getPlayer().getLevel() >= params.get(0);
}
}

View File

@ -2,21 +2,23 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import emu.grasscutter.game.quest.GameQuest;
import org.anime_game_servers.core.gi.enums.QuestState;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_QUEST_FINISH;
import java.util.List;
@ActivityCondition(NEW_ACTIVITY_COND_QUEST_FINISH)
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_QUEST_FINISH;
@ActivityConditionHandler(NEW_ACTIVITY_COND_QUEST_FINISH)
public class QuestFinished extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
GameQuest quest = activityData
.getPlayer()
.getQuestManager()
.getQuestById(params[0]);
.getQuestById(params.get(0));
return quest != null && quest.getState() == QuestState.QUEST_STATE_FINISHED;
}

View File

@ -2,15 +2,17 @@ package emu.grasscutter.game.activity.condition.all;
import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityCondition;
import emu.grasscutter.game.activity.condition.ActivityConditionHandler;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import static emu.grasscutter.game.activity.condition.ActivityConditions.NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER;
import java.util.List;
@ActivityCondition(NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER)
import static org.anime_game_servers.game_data_models.gi.data.activity.ActivityCondition.NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER;
@ActivityConditionHandler(NEW_ACTIVITY_COND_SALESMAN_CAN_DELIVER)
public class SalesmanCanDeliver extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
//TODO need to reverse engineer this logic.
//This condition appears only in one condition "condId": 5003001
//and this condition accept no params. I have no idea how to implement it

View File

@ -5,13 +5,15 @@ import emu.grasscutter.game.activity.ActivityConfigItem;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.activity.condition.ActivityConditionBaseHandler;
import java.util.List;
/**
* This class is used when condition was not found
*/
public class UnknownActivityConditionHandler extends ActivityConditionBaseHandler {
@Override
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
public boolean execute(PlayerActivityData activityData, ActivityConfigItem activityConfig, List<Integer> params) {
Grasscutter.getLogger().error("Called unknown condition handler");
return false;
}

View File

@ -3,12 +3,11 @@ package emu.grasscutter.game.activity.musicgame;
import emu.grasscutter.game.activity.ActivityHandler;
import emu.grasscutter.game.activity.GameActivity;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.utils.JsonUtils;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.anime_game_servers.multi_proto.gi.messages.activity.music_game.MusicGameActivityDetailInfo;
import org.anime_game_servers.multi_proto.gi.messages.activity.user_generated_content.music_game.UgcMusicBriefInfo;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import java.util.stream.Collectors;

View File

@ -2,9 +2,9 @@ package emu.grasscutter.game.activity.musicgame;
import emu.grasscutter.game.activity.ActivityWatcher;
import emu.grasscutter.game.activity.ActivityWatcherType;
import emu.grasscutter.game.props.WatcherTriggerType;
import static org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE;
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
@ActivityWatcherType(TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
public class MusicGameScoreTrigger extends ActivityWatcher {
@Override
protected boolean isMeet(String... param) {

View File

@ -3,12 +3,13 @@ package emu.grasscutter.game.activity.trialavatar;
import emu.grasscutter.game.activity.ActivityWatcher;
import emu.grasscutter.game.activity.ActivityWatcherType;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.props.WatcherTriggerType;
import lombok.val;
import java.util.stream.Stream;
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FINISH_CHALLENGE)
import static org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType.TRIGGER_FINISH_CHALLENGE;
@ActivityWatcherType(TRIGGER_FINISH_CHALLENGE)
public class TrialAvatarActivityChallengeTrigger extends ActivityWatcher {
@Override
protected boolean isMeet(String... param) {

View File

@ -10,11 +10,9 @@ import emu.grasscutter.game.activity.DefaultWatcher;
import emu.grasscutter.game.dungeons.settle_listeners.TrialAvatarDungeonSettleListener;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.activity.ActivityHandler;
import emu.grasscutter.game.activity.GameActivity;
import emu.grasscutter.game.activity.PlayerActivityData;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.server.packet.send.PacketActivityInfoNotify;
import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify;
import emu.grasscutter.utils.JsonUtils;
@ -22,6 +20,8 @@ import emu.grasscutter.utils.JsonUtils;
import java.util.*;
import lombok.*;
import org.anime_game_servers.multi_proto.gi.messages.activity.general.ActivityInfo;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType;
@GameActivity(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR)
public class TrialAvatarActivityHandler extends ActivityHandler {
@ -108,7 +108,7 @@ public class TrialAvatarActivityHandler extends ActivityHandler {
val rewardParam = GameData.getRewardDataMap().get(rewardInfo.getRewardId());
if (rewardParam == null) return false;
player.getInventory().addItemParamDatas(rewardParam.getRewardItemList(), ActionReason.TrialAvatarActivityFirstPassReward);
player.getInventory().addRewardData(rewardParam, ActionReason.TrialAvatarActivityFirstPassReward);
rewardInfo.setReceivedReward(true);
playerActivityData.get().setDetail(trialAvatarPlayerData);
playerActivityData.get().save();

View File

@ -12,6 +12,7 @@ import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import emu.grasscutter.data.custom.AvatarDataCache;
import lombok.*;
import org.anime_game_servers.multi_proto.gi.messages.general.PropValue;
import org.anime_game_servers.multi_proto.gi.messages.general.avatar.*;
@ -45,7 +46,7 @@ public class Avatar {
@Indexed @Getter private int ownerId; // id of player that this avatar belongs to
@Transient private Player owner;
@Transient @Getter protected AvatarData data;
@Transient @Getter protected AvatarDataCache data;
@Transient @Getter protected AvatarSkillDepotData skillDepot;
@Transient @Getter protected long guid; // Player unique id
@Getter protected int avatarId; // id of avatar
@ -89,14 +90,14 @@ public class Avatar {
// On creation
public Avatar(int avatarId) {
this(GameData.getAvatarDataMap().get(avatarId));
this(GameData.getAvatarInfoCacheMap().get(avatarId));
}
public Avatar(AvatarData data) {
public Avatar(AvatarDataCache data) {
this();
this.avatarId = data.getId();
this.nameCardRewardId = data.getNameCardRewardId();
this.nameCardId = data.getNameCardId();
this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.avatarId).getRewardId();
this.nameCardId = GameData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId();
this.data = data;
this.bornTime = (int) (System.currentTimeMillis() / 1000);
this.flyCloak = 140001;
@ -111,7 +112,8 @@ public class Avatar {
.forEach(id -> this.setFightProperty(id, 0f));
// Skill depot
this.setSkillDepotData(data.getSkillDepot(), false);
val skillDepot = data.getSkillDepots().get(data.getDefaultSkillDepotId());
this.setSkillDepotData(skillDepot, false);
// Set stats
this.recalcStats();
@ -130,11 +132,11 @@ public class Avatar {
return id;
}
public AvatarData getAvatarData() {
public AvatarDataCache getAvatarData() {
return data;
}
protected void setAvatarData(AvatarData data) {
protected void setAvatarData(AvatarDataCache data) {
if (this.data != null) return;
this.data = data; // Used while loading this from the database
}
@ -214,7 +216,7 @@ public class Avatar {
* @return false if failed or already using that element, true if it actually changed
*/
public boolean changeElement(@Nonnull ElementType elementTypeToChange) {
val candSkillDepotIdsList = data.getCandSkillDepotIds();
val candSkillDepotIdsList = data.getAvatarData().getCandSkillDepotIds();
val candSkillDepotIndex = elementTypeToChange.getDepotIndex();
// if no candidate skill to change or index out of bound
@ -225,7 +227,7 @@ public class Avatar {
int candSkillDepotId = candSkillDepotIdsList.get(candSkillDepotIndex);
// Sanity checks for skill depots
val skillDepot = GameData.getAvatarSkillDepotDataMap().get(candSkillDepotId);
val skillDepot = data.getSkillDepots().get(candSkillDepotId);
if (skillDepot == null || skillDepot.getId() == skillDepotId) {
return false;
}
@ -386,7 +388,7 @@ public class Avatar {
public void recalcStats(boolean forceSendAbilityChange) {
// Setup
var data = this.getAvatarData();
var promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
var promoteData = data.getPromoteData().get(this.getPromoteLevel());
var setMap = new Int2IntOpenHashMap();
// Extra ability embryos

View File

@ -4,7 +4,6 @@ import java.util.Iterator;
import java.util.List;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.EntityAvatar;
@ -18,6 +17,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Getter;
import lombok.val;
@Getter
public class AvatarStorage extends BasePlayerManager implements Iterable<Avatar> {
@ -147,7 +147,7 @@ public class AvatarStorage extends BasePlayerManager implements Iterable<Avatar>
continue;
}
AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId());
val avatarData = GameData.getAvatarInfoCacheMap().get(avatar.getAvatarId());
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (avatarData == null || skillDepot == null) {
continue;

View File

@ -4,7 +4,6 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.BaseTrialAvatarData;
import emu.grasscutter.data.common.BaseTrialAvatarTemplateData;
import emu.grasscutter.data.excels.AvatarCostumeData;
import emu.grasscutter.data.excels.TrialReliquaryData;
import emu.grasscutter.game.entity.EntityWeapon;
import emu.grasscutter.game.inventory.EquipType;
@ -13,6 +12,7 @@ import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.entities.avatar.AvatarCostumeData;
import org.anime_game_servers.multi_proto.gi.messages.general.avatar.AvatarInfo;
import org.anime_game_servers.multi_proto.gi.messages.general.avatar.GrantReason;
import org.anime_game_servers.multi_proto.gi.messages.general.avatar.TrialAvatarGrantRecord;

View File

@ -15,6 +15,7 @@ import org.anime_game_servers.multi_proto.gi.messages.battle_pass.BattlePassCycl
import org.anime_game_servers.multi_proto.gi.messages.battle_pass.BattlePassRewardTakeOption;
import org.anime_game_servers.multi_proto.gi.messages.battle_pass.BattlePassSchedule;
import org.anime_game_servers.multi_proto.gi.messages.battle_pass.BattlePassUnlockStatus;
import org.anime_game_servers.game_data_models.gi.data.quest.GainItem;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
@ -23,10 +24,8 @@ import dev.morphia.annotations.Indexed;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.BattlePassRewardData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.RewardData;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.inventory.MaterialType;
@ -191,7 +190,7 @@ public class BattlePassManager extends BasePlayerDataManager {
}
}
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, ItemParamData entry, List<GameItem> rewardItems) {
private void takeRewardsFromSelectChest(ItemData rewardItemData, int index, GainItem entry, List<GameItem> rewardItems) {
// Sanity checks.
if (rewardItemData.getItemUse().size() < 1) {
return;
@ -209,15 +208,15 @@ public class BattlePassManager extends BasePlayerDataManager {
// For ITEM_USE_ADD_SELECT_ITEM chests, we can directly add the item specified in the chest's data.
if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_ADD_SELECT_ITEM) {
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(chosenId), entry.getItemCount());
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(chosenId), entry.getCount());
rewardItems.add(rewardItem);
}
// For ITEM_USE_GRANT_SELECT_REWARD chests, we have to again look up reward data.
else if (rewardItemData.getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_GRANT_SELECT_REWARD) {
RewardData selectedReward = GameData.getRewardDataMap().get(chosenId);
val selectedReward = GameData.getRewardDataMap().get(chosenId);
for (var r : selectedReward.getRewardItemList()) {
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getItemCount());
GameItem rewardItem = new GameItem(GameData.getItemDataMap().get(r.getItemId()), r.getCount());
rewardItems.add(rewardItem);
}
}
@ -265,7 +264,7 @@ public class BattlePassManager extends BasePlayerDataManager {
int index = option.getOptionIdx();
// Make sure we have reward data.
RewardData reward = GameData.getRewardDataMap().get(tag.getRewardId());
val reward = GameData.getRewardDataMap().get(tag.getRewardId());
if (reward == null) {
continue;
}
@ -280,7 +279,7 @@ public class BattlePassManager extends BasePlayerDataManager {
}
// All other rewards directly give us the right item.
else {
GameItem rewardItem = new GameItem(rewardItemData, entry.getItemCount());
GameItem rewardItem = new GameItem(rewardItemData, entry.getCount());
rewardItems.add(rewardItem);
}
}

View File

@ -2,18 +2,14 @@ package emu.grasscutter.game.dungeons;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.DungeonElementChallengeData;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.LogicTypeUtils;
import emu.grasscutter.game.activity.trialavatar.TrialAvatarActivityHandler;
import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ActivityType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.quest.enums.LogicType;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.packet.send.PacketDungeonDataNotify;
@ -30,14 +26,20 @@ import lombok.Setter;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.general.avatar.GrantReason;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.WeeklyBossResinDiscountInfo;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
import org.anime_game_servers.gi_lua.models.ScriptArgs;
import org.anime_game_servers.gi_lua.models.constants.EventType;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static org.anime_game_servers.game_data_models.gi.data.general.UnsetTypesKt.UnsetInt;
/**
* TODO handle time limits
* TODO handle respawn points
@ -48,7 +50,7 @@ public class DungeonManager {
@Getter private final Scene scene;
@Getter private final DungeonData dungeonData;
@Getter private final DungeonPassConfigData passConfigData;
@Getter private final DungeonPassData passConfigData;
@Getter private final int[] finishedConditions;
@Getter private final IntSet rewardedPlayers = new IntOpenHashSet();
@ -76,7 +78,7 @@ public class DungeonManager {
}
public boolean isFinishedSuccessfully() {
return LogicType.calculate(this.passConfigData.getLogicType(), this.finishedConditions);
return LogicTypeUtils.calculate(this.passConfigData.getLogicTypeNonNull(), this.finishedConditions);
}
public int getLevelForMonster(int id) {
@ -120,7 +122,7 @@ public class DungeonManager {
private boolean hasRewards(){
//TODO check dungeonData.passRewardId and dungeondata.passRewardPreviewId only as fallback
return this.dungeonData.getRewardPreviewData() != null && this.dungeonData.getRewardPreviewData().getPreviewItems().length > 0;
return this.dungeonData.getPassRewardPreviewId()!=UnsetInt || this.dungeonData.getPassRewardId()!=UnsetInt;
}
private boolean hasPlayerClaimedRewards(Player player){
@ -159,7 +161,7 @@ public class DungeonManager {
// Spend the condensed resin and only proceed if the transaction succeeds.
return player.getResinManager().useCondensedResin(1);
} else if (this.dungeonData.getStatueCostID() == 106) {
} else if (this.dungeonData.getStatueCostId() == 106) {
// Spend the resin and only proceed if the transaction succeeds.
return player.getResinManager().useResin(resinCost);
} //TODO should we maybe support other currencies here?
@ -170,6 +172,7 @@ public class DungeonManager {
val rewards = new ArrayList<GameItem>();
final int dungeonId = this.dungeonData.getId();
// If we have specific drop data for this dungeon, we use it.
// TODO use statueDrop for this, in combination with DropTable and leaf data
if (GameData.getDungeonDropDataMap().containsKey(dungeonId)) {
val dropEntries = GameData.getDungeonDropDataMap().get(dungeonId);
@ -209,8 +212,12 @@ public class DungeonManager {
// Otherwise, we fall back to the preview data.
else {
Grasscutter.getLogger().info("No drop data found or dungeon {}, falling back to preview data ...", dungeonId);
Arrays.stream(this.dungeonData.getRewardPreviewData().getPreviewItems())
.map(param -> new GameItem(param.getId(), Math.max(param.getCount(), 1)))
//val rewardData = GameData.getRewardDataMap().get(this.dungeonData.getPassRewardId());
val rewardPreviewData = GameData.getRewardDataMap().get(this.dungeonData.getPassRewardPreviewId());
rewardPreviewData.getRewardItemList().stream()
.map(param -> new GameItem(param.getItemId(), Math.max(param.getCount(), 1)))
.forEach(rewards::add);
}
@ -266,7 +273,7 @@ public class DungeonManager {
this.dungeonData.getId());
// Battle pass trigger
if (this.dungeonData.getType().isCountsToBattlepass() && successfully) {
if (successfully) {
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
}
if(dungeonData.getPassJumpDungeon() > 0){

View File

@ -4,24 +4,24 @@ import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
import emu.grasscutter.game.dungeons.dungeon_entry.DungeonEntries;
import emu.grasscutter.game.dungeons.dungeon_entry.PlayerDungeonExitInfo;
import emu.grasscutter.game.dungeons.enums.DungeonType;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import emu.grasscutter.game.dungeons.pass_condition.BaseCondition;
import emu.grasscutter.game.dungeons.settle_listeners.BasicDungeonSettleListener;
import emu.grasscutter.game.dungeons.settle_listeners.DungeonSettleListener;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.server.game.BaseGameSystem;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonType;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneType;
import org.reflections.Reflections;
import java.util.Optional;
@ -56,7 +56,7 @@ public class DungeonSystem extends BaseGameSystem {
});
}
public boolean triggerCondition(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
public boolean triggerCondition(DungeonPassData.DungeonPassCondition condition, int... params) {
val handler = this.passCondHandlers.get(condition.getCondType().ordinal());
if (handler == null) {

View File

@ -1,6 +1,6 @@
package emu.grasscutter.game.dungeons;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -2,12 +2,10 @@ package emu.grasscutter.game.dungeons.challenge;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.dungeons.challenge.trigger.*;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.props.ElementReactionType;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
@ -15,10 +13,12 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.gi_lua.models.ScriptArgs;
import org.anime_game_servers.gi_lua.models.constants.EventType;
import org.anime_game_servers.gi_lua.models.scene.group.SceneGroup;
import org.anime_game_servers.gi_lua.models.scene.group.SceneTrigger;
import org.anime_game_servers.game_data_models.gi.data.watcher.WatcherTriggerType;
import java.util.ArrayList;
import java.util.List;

View File

@ -3,11 +3,11 @@ package emu.grasscutter.game.dungeons.dungeon_entry;
import dev.morphia.annotations.Entity;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.DungeonRosterData;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
import lombok.val;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import java.time.*;
import java.time.temporal.TemporalAdjusters;

View File

@ -3,7 +3,6 @@ package emu.grasscutter.game.dungeons.dungeon_entry;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.ScenePointEntry;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.data.excels.DungeonRosterData;
import emu.grasscutter.data.excels.DungeonSerialData;
import emu.grasscutter.game.player.BasePlayerManager;
@ -17,6 +16,7 @@ import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.entry.DungeonEntryInfo;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.WeeklyBossResinDiscountInfo;
import org.anime_game_servers.core.gi.enums.QuestState;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import java.util.*;
import java.util.stream.Stream;

View File

@ -1,14 +1,13 @@
package emu.grasscutter.game.dungeons.dungeon_entry;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.excels.SceneData;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.SceneType;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import lombok.*;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneData;
import org.anime_game_servers.game_data_models.gi.data.scene.SceneType;
import java.util.Optional;
@ -27,7 +26,7 @@ public class PlayerDungeonExitInfo {
}
public void setAll(Player player, int dungeonId, int pointId) {
val fromBigWorld = Optional.ofNullable(player.getScene()).map(Scene::getSceneData).map(SceneData::getSceneType)
val fromBigWorld = Optional.ofNullable(player.getScene()).map(Scene::getSceneData).map(SceneData::getType)
.filter(sceneType -> sceneType == SceneType.SCENE_WORLD);
if (fromBigWorld.isEmpty()) return; // only set player exits location if player comes in from big world

View File

@ -1,6 +1,5 @@
package emu.grasscutter.game.dungeons.dungeon_results;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.dungeons.DungeonEndStats;
import emu.grasscutter.game.entity.EntityAvatar;
@ -14,12 +13,12 @@ import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.StrengthenPointData;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.progression.DungeonSettleNotify;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.progression.ParamList;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.InvolveType;
import java.util.*;
import java.util.stream.Collectors;
import static emu.grasscutter.game.dungeons.enums.DungeonInvolveType.INVOLVE_SINGLE_MULTIPLE;
/**
* Shows dungeon results
* */
@ -46,7 +45,7 @@ public class BaseDungeonResult {
* */
private void getStrengthenPointData(DungeonSettleNotify builder) {
if (this.dungeonStats.dungeonResult().isSuccess() ||
this.dungeonData.getInvolveType() != INVOLVE_SINGLE_MULTIPLE) return;
this.dungeonData.getInvolveType() != InvolveType.INVOLVE_SINGLE_MULTIPLE) return;
val playerActiveTeam = this.player.getTeamManager().getActiveTeam();
builder.setStrengthenPointDataMap(Arrays.stream(StrengthenPointType.values()).collect(

View File

@ -1,6 +1,5 @@
package emu.grasscutter.game.dungeons.dungeon_results;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.game.dungeons.DungeonEndStats;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
@ -9,6 +8,7 @@ import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.progression.DungeonSettleNotify;
import org.anime_game_servers.multi_proto.gi.messages.spiral_abyss.run.ContinueStateType;
import org.anime_game_servers.multi_proto.gi.messages.spiral_abyss.run.TowerLevelEndNotify;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
import java.util.List;
import java.util.stream.IntStream;

View File

@ -1,12 +1,12 @@
package emu.grasscutter.game.dungeons.dungeon_results;
import emu.grasscutter.data.excels.DungeonData;
import emu.grasscutter.game.dungeons.DungeonEndStats;
import emu.grasscutter.game.player.Player;
import lombok.Builder;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.activity.trial.TrialAvatarFirstPassDungeonNotify;
import org.anime_game_servers.multi_proto.gi.messages.dungeon.progression.DungeonSettleNotify;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonData;
public class TrialAvatarDungeonResult extends BaseDungeonResult {
int trialCharacterIndexId;

View File

@ -1,7 +0,0 @@
package emu.grasscutter.game.dungeons.enums;
public enum DungeonInvolveType {
INVOLVE_NONE,
INVOLVE_ONLY_SINGLE,
INVOLVE_SINGLE_MULTIPLE
}

View File

@ -1,27 +0,0 @@
package emu.grasscutter.game.dungeons.enums;
import lombok.Getter;
import org.anime_game_servers.core.base.interfaces.IntValueEnum;
public enum DungeonPassConditionType implements IntValueEnum {
DUNGEON_COND_NONE(0),
DUNGEON_COND_KILL_MONSTER(3),
DUNGEON_COND_KILL_GROUP_MONSTER(5),
DUNGEON_COND_KILL_TYPE_MONSTER(7),
DUNGEON_COND_FINISH_QUEST(9),
DUNGEON_COND_KILL_MONSTER_COUNT(11), // TODO handle count
DUNGEON_COND_IN_TIME(13), // Missing triggers and tracking
DUNGEON_COND_FINISH_CHALLENGE(14),
DUNGEON_COND_END_MULTISTAGE_PLAY(15) // Missing
;
@Getter private final int id;
DungeonPassConditionType(int id){
this.id = id;
}
@Override
public int getValue() {
return id;
}
}

View File

@ -1,7 +0,0 @@
package emu.grasscutter.game.dungeons.enums;
public enum DungeonPlayType {
DUNGEON_PLAY_TYPE_NONE,
DUNGEON_PLAY_TYPE_FOGGY_MAZE,
DUNGEON_PLAY_TYPE_MIST_TRIAL,
DUNGEON_PLAY_TYPE_TRIAL_AVATAR
}

View File

@ -1,49 +0,0 @@
package emu.grasscutter.game.dungeons.enums;
import lombok.Getter;
public enum DungeonType {
DUNGEON_NONE(false),
DUNGEON_PLOT(true),
DUNGEON_FIGHT(true),
DUNGEON_DAILY_FIGHT(false),
DUNGEON_WEEKLY_FIGHT(true),
DUNGEON_DISCARDED(false),
DUNGEON_TOWER(false),
DUNGEON_BOSS(true),
DUNGEON_ACTIVITY(false),
DUNGEON_EFFIGY(false),
DUNGEON_ELEMENT_CHALLENGE(true),
DUNGEON_THEATRE_MECHANICUS(false),
DUNGEON_FLEUR_FAIR(false),
DUNGEON_CHANNELLER_SLAB_LOOP(false),
DUNGEON_CHANNELLER_SLAB_ONE_OFF(false),
DUNGEON_BLITZ_RUSH(true),
DUNGEON_CHESS(false),
DUNGEON_SUMO_COMBAT(false),
DUNGEON_ROGUELIKE(false),
DUNGEON_HACHI(false),
DUNGEON_POTION(false),
DUNGEON_MINI_ELDRITCH(false),
DUNGEON_UGC(false),
DUNGEON_GCG(false),
DUNGEON_CRYSTAL_LINK(false),
DUNGEON_IRODORI_CHESS(false),
DUNGEON_ROGUE_DIARY(false),
DUNGEON_DREAMLAND(false),
DUNGEON_SUMMER_V2(true),
DUNGEON_MUQADAS_POTION(false),
DUNGEON_INSTABLE_SPRAY(false),
DUNGEON_WIND_FIELD(false),
DUNGEON_BIGWORLD_MIRROR(false),
DUNGEON_FUNGUS_FIGHTER_TRAINING(false),
DUNGEON_FUNGUS_FIGHTER_PLOT(false),
DUNGEON_EFFIGY_CHALLENGE_V2(false),
DUNGEON_CHAR_AMUSEMENT(false);
@Getter private final boolean countsToBattlepass;
DungeonType(boolean countsToBattlepass){
this.countsToBattlepass = countsToBattlepass;
}
}

View File

@ -1,17 +0,0 @@
package emu.grasscutter.game.dungeons.enums;
import lombok.Getter;
public enum SettleShowType {
SETTLE_SHOW_NONE(0),
SETTLE_SHOW_TIME_COST(1),
SETTLE_SHOW_OPEN_CHEST_COUNT(2),
SETTLE_SHOW_KILL_MONSTER_COUNT(3),
SETTLE_SHOW_BLACKSCREEN(4);
@Getter private final int id;
SettleShowType(int id){
this.id = id;
}
}

View File

@ -1,9 +1,9 @@
package emu.grasscutter.game.dungeons.handlers;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
public abstract class DungeonBaseHandler {
public abstract boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params);
public abstract boolean execute(DungeonPassData.DungeonPassCondition condition, int... params);
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_NONE)
public class BaseCondition extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
// TODO Auto-generated method stub
return false;
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_CHALLENGE)
public class ConditionFinishChallenge extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam()[0] || params[1] == condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam().get(0) || params[1] == condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_FINISH_QUEST)
public class ConditionFinishQuest extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_IN_TIME)
public class ConditionInTime extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] <= condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] <= condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_GROUP_MONSTER)
public class ConditionKillGroupMonster extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER)
public class ConditionKillMonster extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_MONSTER_COUNT)
public class ConditionKillMonsterCount extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] >= condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] >= condition.getParam().get(0);
}
}

View File

@ -1,15 +1,15 @@
package emu.grasscutter.game.dungeons.pass_condition;
import emu.grasscutter.data.excels.DungeonPassConfigData;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.dungeons.DungeonValue;
import emu.grasscutter.game.dungeons.handlers.DungeonBaseHandler;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassData;
@DungeonValue(DungeonPassConditionType.DUNGEON_COND_KILL_TYPE_MONSTER)
public class ConditionKillTypeMonster extends DungeonBaseHandler {
@Override
public boolean execute(DungeonPassConfigData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam()[0];
public boolean execute(DungeonPassData.DungeonPassCondition condition, int... params) {
return params[0] == condition.getParam().get(0);
}
}

View File

@ -7,10 +7,7 @@ import emu.grasscutter.game.dungeons.dungeon_results.BaseDungeonResult;
import emu.grasscutter.game.dungeons.dungeon_results.TrialAvatarDungeonResult;
import emu.grasscutter.server.packet.send.PacketDungeonSettleNotify;
import lombok.val;
import java.util.Optional;
import static emu.grasscutter.game.props.ActivityType.NEW_ACTIVITY_TRIAL_AVATAR;
import org.anime_game_servers.game_data_models.gi.data.activity.ActivityType;
public class TrialAvatarDungeonSettleListener implements DungeonSettleListener{
@Override
@ -24,7 +21,7 @@ public class TrialAvatarDungeonSettleListener implements DungeonSettleListener{
.setDungeonStats(new DungeonEndStats(scene.getKilledMonsterCount(), time, 0, endReason))
.setPlayer(hostPlayer)
.setTrialCharacterIndexId(hostPlayer.getActivityManager()
.getActivityHandlerAs(NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class)
.getActivityHandlerAs(ActivityType.NEW_ACTIVITY_TRIAL_AVATAR, TrialAvatarActivityHandler.class)
.map(TrialAvatarActivityHandler::getSelectedTrialAvatarIndex).orElse(0))
.build()));
}

View File

@ -2,7 +2,7 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.GameConstants;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.EquipType;
@ -261,7 +261,7 @@ public class EntityAvatar extends GameEntity {
}
public AbilityControlBlock getAbilityControlBlock() {
AvatarData data = this.getAvatar().getAvatarData();
val data = this.getAvatar().getAvatarData();
val abilityControlBlock = new AbilityControlBlock();
val embryoId = new AtomicInteger(0);
val embrioList = new ArrayList<AbilityEmbryo>();
@ -269,8 +269,28 @@ public class EntityAvatar extends GameEntity {
val abilities = data.getAbilities();
// Add avatar abilities
if (abilities != null) {
embrioList.addAll(abilities.stream().map(id -> new AbilityEmbryo(embryoId.incrementAndGet(), id, GameConstants.DEFAULT_ABILITY_NAME)).toList());
embrioList.addAll(abilities.intStream()
.mapToObj(id -> new AbilityEmbryo(embryoId.incrementAndGet(), id, GameConstants.DEFAULT_ABILITY_NAME))
.toList());
}
// Add level entity abilities
val sceneData = getScene().getSceneData();
if(sceneData!=null) {
val specifiedAvatars = sceneData.getSpecifiedAvatarList();
if (specifiedAvatars == null || specifiedAvatars.isEmpty() || specifiedAvatars.contains(data.getAvatarId())){
val config = GameData.getConfigLevelEntityDataMap().get(sceneData.getLevelEntityConfig());
if(config != null && config.getMonsterAbilities() != null) {
val configAbilitiesList = config.getAvatarAbilities().stream()
.map(ConfigAbilityData::getAbilityName)
.map(Utils::abilityHash)
.map(id -> new AbilityEmbryo(embryoId.incrementAndGet(), id, GameConstants.DEFAULT_ABILITY_NAME))
.toList();
embrioList.addAll(configAbilitiesList);
}
}
}
// Add default abilities
embrioList.addAll(Arrays.stream(GameConstants.DEFAULT_ABILITY_HASHES).mapToObj(id -> new AbilityEmbryo(embryoId.incrementAndGet(), id, GameConstants.DEFAULT_ABILITY_NAME)).toList());
// Add team resonances
@ -281,7 +301,7 @@ public class EntityAvatar extends GameEntity {
embrioList.addAll(skillDepot.getAbilities().stream().map(id -> new AbilityEmbryo(embryoId.incrementAndGet(), id, GameConstants.DEFAULT_ABILITY_NAME)).toList());
}
// Add equip abilities
if (this.getAvatar().getExtraAbilityEmbryos().size() > 0) {
if (!this.getAvatar().getExtraAbilityEmbryos().isEmpty()) {
embrioList.addAll(this.getAvatar().getExtraAbilityEmbryos().stream().map(id -> new AbilityEmbryo(embryoId.incrementAndGet(), Utils.abilityHash(id), GameConstants.DEFAULT_ABILITY_NAME)).toList());
}
abilityControlBlock.setAbilityEmbryoList(embrioList);

View File

@ -9,7 +9,6 @@ import emu.grasscutter.data.excels.MonsterAffixData;
import emu.grasscutter.data.excels.MonsterCurveData;
import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.game.ability.AbilityManager;
import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.entity.interfaces.StringAbilityEntity;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.*;
@ -28,6 +27,7 @@ import org.anime_game_servers.multi_proto.gi.messages.gadget.GadgetInteractReq;
import org.anime_game_servers.multi_proto.gi.messages.general.ability.AbilitySyncStateInfo;
import org.anime_game_servers.multi_proto.gi.messages.general.entity.SceneWeaponInfo;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.*;
import org.anime_game_servers.game_data_models.gi.data.dungeon.DungeonPassConditionType;
import org.anime_game_servers.gi_lua.models.ScriptArgs;
import org.anime_game_servers.gi_lua.models.constants.EventType;
import org.anime_game_servers.gi_lua.models.scene.group.SceneGroup;

View File

@ -1,5 +1,7 @@
package emu.grasscutter.game.inventory;
import java.util.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
@ -21,6 +23,8 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.val;
import org.anime_game_servers.multi_proto.gi.messages.general.item.ItemParam;
import org.anime_game_servers.game_data_models.gi.data.quest.GainItem;
import org.anime_game_servers.game_data_models.gi.data.rewards.RewardData;
import javax.annotation.Nullable;
import java.util.ArrayList;
@ -158,7 +162,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
public void addItems(Collection<GameItem> items, ActionReason reason) {
List<GameItem> changedItems = new ArrayList<>();
for (var item : items) {
if (item.getItemId() == 0) continue;
if (item.getItemId() <= 0) continue;
GameItem result = null;
try {
// putItem might throws exception
@ -202,6 +206,45 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
public void addItemParamDatas(Collection<ItemParamData> items, ActionReason reason) {
addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason);
}
private Collection<GameItem> rewardDataToGameItems(RewardData rewardData){
val rewards = new ArrayList<GameItem>(rewardData.getRewardItemList().size());
rewards.addAll(rewardData.getRewardItemList().stream().filter(Objects::nonNull)
.filter(gainItem -> gainItem.getItemId() > 0)
.map(param -> new GameItem(param.getItemId(), param.getCount()))
.toList());
if(rewardData.getPlayerExp() != 0){
rewards.add(new GameItem(102, rewardData.getPlayerExp()));
}
if(rewardData.getHcoin() > 0){
rewards.add(new GameItem(201, rewardData.getHcoin()));
}
if (rewardData.getScoin() > 0) {
rewards.add(new GameItem(202, rewardData.getScoin()));
}
if(rewardData.getCharacterExp() > 0) {
rewards.add(new GameItem(101, rewardData.getCharacterExp()));
}
if (rewardData.getFriendshipExp() > 0) {
rewards.add(new GameItem(105, rewardData.getFriendshipExp()));
}
if (rewardData.getResin() > 0) {
rewards.add(new GameItem(106, rewardData.getResin()));
}
// TODO handle ITEM_USE_ADD_SELECT_ITEM and ITEM_USE_GRANT_SELECT_REWARD via index parameter
return rewards;
}
public void addRewardData(RewardData rewardData, ActionReason reason) {
val rewardItems = rewardDataToGameItems(rewardData);
addItems(rewardItems, reason);
}
public List<GameItem> addRewardData(List<RewardData> rewardData, ActionReason reason) {
val totalRewards = new ArrayList<GameItem>();
for (val reward : rewardData) {
totalRewards.addAll(rewardDataToGameItems(reward));
}
addItems(totalRewards, reason);
return totalRewards;
}
private synchronized GameItem putItem(GameItem item) {
// Dont add items that dont have a valid item definition.
@ -360,6 +403,9 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
return true;
}
public boolean payItem(GainItem costItem) {
return this.payItem(costItem.getItemId(), costItem.getCount());
}
public boolean payItem(ItemParamData costItem) {
return this.payItem(costItem.getId(), costItem.getCount());
}
@ -389,6 +435,19 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
return true;
}
public synchronized boolean payItems(GainItem[] costItems, ActionReason reason) {
// Make sure player has requisite items
for (var cost : costItems)
if (this.getVirtualItemCount(cost.getItemId()) < (cost.getCount()))
return false;
// All costs are satisfied, now remove them all
for (var cost : costItems) {
this.payVirtualItem(cost.getItemId(), cost.getCount());
}
return true;
}
public boolean payItems(Iterable<ItemParamData> costItems) {
return this.payItems(costItems, 1, null);
}

View File

@ -2,7 +2,6 @@ package emu.grasscutter.game.managers;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.CityData;
import emu.grasscutter.game.city.CityInfoData;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.BasePlayerManager;
@ -11,6 +10,7 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.utils.Language;
import org.anime_game_servers.multi_proto.gi.messages.general.Retcode;
import org.anime_game_servers.multi_proto.gi.messages.scene.entity.ChangeHpReason;
import org.anime_game_servers.multi_proto.gi.messages.general.PropChangeReason;
@ -20,14 +20,17 @@ import emu.grasscutter.server.packet.send.PacketSceneForceUnlockNotify;
import lombok.val;
import emu.grasscutter.server.packet.send.PacketLevelupCityRsp;
import org.anime_game_servers.game_data_models.gi.data.city.CityData;
import org.anime_game_servers.game_data_models.gi.data.city.CityLevelUpData;
import org.anime_game_servers.gi_lua.models.ScriptArgs;
import org.anime_game_servers.gi_lua.models.constants.EventType;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.*;
// Statue of the Seven Manager
/*
* Manages the Statue of the Seven and CityLevel
*/
public class SotSManager extends BasePlayerManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
@ -112,7 +115,8 @@ public class SotSManager extends BasePlayerManager {
if (isAlive) {
return;
}
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
val name = entity.getAvatar().getAvatarData().getBaseName();
logger.trace("Reviving avatar {}", name);
player.getTeamManager().reviveAvatar(entity.getAvatar());
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
});
@ -165,7 +169,8 @@ public class SotSManager extends BasePlayerManager {
setCurrentVolume(0);
}
if (needHP > 0) {
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
val name = entity.getAvatar().getAvatarData().getBaseName();
logger.trace("Healing avatar {} + {}", name, needHP);
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_STATUE_RECOVER,
@ -200,9 +205,11 @@ public class SotSManager extends BasePlayerManager {
}
}
// City/Statue level handling
public CityData getCityByAreaId(int areaId) {
return GameData.getCityDataMap().values().stream()
.filter(city -> city.getAreaIdVec().contains(areaId))
.filter(city -> city.getAreaIds().contains(areaId))
.findFirst()
.orElse(null);
}
@ -228,62 +235,107 @@ public class SotSManager extends BasePlayerManager {
// search city by areaId
var city = this.getCityByAreaId(areaId);
if (city == null) return;
if (city == null) return; // TODO return error to allow sending packet with error
var cityId = city.getCityId();
// check data level up
var cityInfo = this.getCityInfo(cityId);
var nextStatuePromoteData = GameData.getStatuePromoteData(cityId, cityInfo.getLevel() + 1);
if (nextStatuePromoteData == null) return;
var nextLevelCrystal = nextStatuePromoteData.getCostItems()[0].getCount();
// delete item from inventory
var itemNumrequired = Math.min(itemNum, nextLevelCrystal - cityInfo.getNumCrystal());
player
.getInventory()
.removeItemById(nextStatuePromoteData.getCostItems()[0].getId(), itemNumrequired);
// update number oculi
cityInfo.setNumCrystal(cityInfo.getNumCrystal() + itemNumrequired);
// hanble quest
if (itemNumrequired >= 1)
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CITY_LEVEL_UP, cityId, areaId);
// handle oculi overflow
if (cityInfo.getNumCrystal() >= nextLevelCrystal) {
cityInfo.setNumCrystal(cityInfo.getNumCrystal() - nextLevelCrystal);
cityInfo.setLevel(cityInfo.getLevel() + 1);
// update max stamina and notify client
player.setProperty(
PlayerProperty.PROP_MAX_STAMINA,
player.getProperty(PlayerProperty.PROP_MAX_STAMINA)
+ nextStatuePromoteData.getStamina() * 100,
true);
// Add items to inventory
if (nextStatuePromoteData.getRewardIdList() != null) {
for (var rewardId : nextStatuePromoteData.getRewardIdList()) {
val rewardData = GameData.getRewardDataMap().get(rewardId);
if (rewardData == null) continue;
player
.getInventory()
.addItemParamDatas(rewardData.getRewardItemList(), ActionReason.CityLevelupReward);
}
val completedLevels = new ArrayList<CityLevelUpData>();
var level = cityInfo.getLevel();
var prevCrystalProgress = cityInfo.getNumCrystal();
var itemCountLeft = itemNum;
var crystalProgress = 0;
while(level<10 && itemCountLeft > 0){
val nextLevelUpData = GameData.getCityLevelUpData(cityId, level + 1);
if(nextLevelUpData == null || nextLevelUpData.getConsumeItem() == null){
break;
}
// unlock forcescene
player.sendPacket(new PacketSceneForceUnlockNotify(1, true));
val cost = nextLevelUpData.getConsumeItem().getCount() - prevCrystalProgress;
// TODO properly include already added oculi for the cost calculations
// player might not want to level up all he can
if(cost > itemCountLeft){
// pay the rest the payer wanted to offer
if(getPlayer().getInventory().payItem(nextLevelUpData.getConsumeItem().getItemId(), itemCountLeft)){
crystalProgress = itemCountLeft + prevCrystalProgress;
itemCountLeft = 0;
}
break;
}
if(getPlayer().getInventory().payItem(nextLevelUpData.getConsumeItem().getItemId(), cost)){
completedLevels.add(nextLevelUpData);
level++;
itemCountLeft -= cost;
prevCrystalProgress = 0;
} else {
break;
}
}
// update number oculi
cityInfo.setNumCrystal(crystalProgress);
cityInfo.setLevel(level);
completedLevels.forEach(levelData -> {
// update player properties
val reward = GameData.getRewardDataMap().get(levelData.getRewardId());
if(reward != null){
getPlayer().getInventory().addRewardData(reward, ActionReason.CityLevelupReward);
}
if(levelData.getActions()!=null) {
handleLevelUpActions(levelData.getActions());
}
});
// update data
this.addCityInfo(cityInfo);
handleLevelUpEvents(cityId, level);
// Packets
player.sendPacket(
new PacketLevelupCityRsp(
sceneId, cityInfo.getLevel(), cityId, cityInfo.getNumCrystal(), areaId, Retcode.RET_SUCC));
sceneId, level, cityId, crystalProgress, areaId, Retcode.RET_SUCC));
}
private void handleLevelUpActions(List<CityLevelUpData.CityLevelUpAction> actions){
actions.forEach(action -> {
if(action.getType() == null) return;
switch (action.getType()) {
case WORLD_AREA_ACTION_IMPROVE_STAMINA -> {
if (action.getParam1() == null || action.getParam1().isEmpty()) {
break;
}
// update max stamina and notify client
getPlayer().setProperty(
PlayerProperty.PROP_MAX_STAMINA,
getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA)
+ action.getParam1().get(0) * 100,
true);
}
case WORLD_AREA_ACTION_UNLOCK_FORCE -> {
if (action.getParam1() == null || action.getParam1().isEmpty()) {
break;
}
// this might need to be persisted
getPlayer().getScene().unlockForce(action.getParam1().get(0));
}
case WORLD_AREA_ACTION_ACTIVATE_ITEM -> {
// TODO: implement
}
default -> {
}
}
});
}
private void handleLevelUpEvents(int cityId, int level) {
player.getQuestManager().queueEvent(QuestContent.QUEST_CONTENT_CITY_LEVEL_UP, cityId, level);
player.getScene().getScriptManager().callEvent(new ScriptArgs(0, EventType.EVENT_CITY_LEVELUP, cityId, level));
}
}

View File

@ -13,6 +13,8 @@ import org.anime_game_servers.multi_proto.gi.messages.blossom.BlossomScheduleInf
import emu.grasscutter.scripts.ScriptSystem;
import emu.grasscutter.utils.Position;
import lombok.*;
import org.anime_game_servers.game_data_models.gi.data.city.CityData;
import org.anime_game_servers.game_data_models.gi.data.world.WorldLevelData;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;

View File

@ -183,11 +183,12 @@ public class EnergyManager extends BasePlayerManager {
// - Does this really count every individual hit separately?
// Get the avatar's weapon type.
WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
val weaponType = avatar.getAvatar().getAvatarData().getWeaponType();
val weaponTypeGC = WeaponType.getTypeByName(weaponType.name());
// Check if we already have probability data for this avatar. If not, insert it.
if (!this.avatarNormalProbabilities.containsKey(avatar)) {
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
this.avatarNormalProbabilities.put(avatar, weaponTypeGC.getEnergyGainInitialProbability());
}
// Roll for energy.
@ -197,11 +198,11 @@ public class EnergyManager extends BasePlayerManager {
// If the player wins the roll, we increase the avatar's energy and reset the probability.
if (roll < currentProbability) {
avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_ABILITY, true);
this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability());
this.avatarNormalProbabilities.put(avatar, weaponTypeGC.getEnergyGainInitialProbability());
}
// Otherwise, we increase the probability for the next hit.
else {
this.avatarNormalProbabilities.put(avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability());
this.avatarNormalProbabilities.put(avatar, currentProbability + weaponTypeGC.getEnergyGainIncreaseProbability());
}
}

Some files were not shown because too many files have changed in this diff Show More