diff --git a/src/main/java/emu/lunarcore/data/ResourceLoader.java b/src/main/java/emu/lunarcore/data/ResourceLoader.java index d22a570..60912f5 100644 --- a/src/main/java/emu/lunarcore/data/ResourceLoader.java +++ b/src/main/java/emu/lunarcore/data/ResourceLoader.java @@ -22,6 +22,7 @@ import emu.lunarcore.data.ResourceDeserializers.LunarRailHashDeserializer; import emu.lunarcore.data.config.FloorInfo; import emu.lunarcore.data.config.FloorInfo.FloorGroupSimpleInfo; import emu.lunarcore.data.config.GroupInfo; +import emu.lunarcore.data.config.SkillAbilityInfo; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; public class ResourceLoader { @@ -42,6 +43,8 @@ public class ResourceLoader { loadResources(); // Load floor infos after resources loadFloorInfos(); + // Load maze abilities + loadMazeAbilities(); // Done loaded = true; @@ -208,4 +211,28 @@ public class ResourceLoader { // Done LunarRail.getLogger().info("Loaded " + GameData.getFloorInfos().size() + " FloorInfos."); } + + // Might be better to cache + private static void loadMazeAbilities() { + int count = 0; + + for (var avatarExcel : GameData.getAvatarExcelMap().values()) { + // Get file + File file = new File(LunarRail.getConfig().getResourceDir() + "/Config/ConfigAdventureAbility/LocalPlayer/LocalPlayer_" + avatarExcel.getNameKey() + "_Ability.json"); + if (!file.exists()) continue; + + try (FileReader reader = new FileReader(file)) { + SkillAbilityInfo avatarSkills = gson.fromJson(reader, SkillAbilityInfo.class); + + if (avatarSkills.parse(avatarExcel)) { + count++; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Done + LunarRail.getLogger().info("Loaded " + count + " maze abilities for avatars"); + } } diff --git a/src/main/java/emu/lunarcore/data/config/DynamicFloat.java b/src/main/java/emu/lunarcore/data/config/DynamicFloat.java new file mode 100644 index 0000000..42172c5 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/DynamicFloat.java @@ -0,0 +1,13 @@ +package emu.lunarcore.data.config; + +public class DynamicFloat { + private boolean IsDynamic; + private double FixedValue; + + public double getValue() { + if (!IsDynamic) { + return FixedValue; + } + return 15; + } +} diff --git a/src/main/java/emu/lunarcore/data/config/SkillAbilityInfo.java b/src/main/java/emu/lunarcore/data/config/SkillAbilityInfo.java new file mode 100644 index 0000000..06c4fdb --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/SkillAbilityInfo.java @@ -0,0 +1,76 @@ +package emu.lunarcore.data.config; + +import java.util.List; + +import emu.lunarcore.data.excel.AvatarExcel; +import emu.lunarcore.game.battle.skills.MazeSkill; +import emu.lunarcore.game.battle.skills.MazeSkillAction; +import emu.lunarcore.game.battle.skills.MazeSkillAddBuff; +import lombok.Getter; + +/** + * The equivalent of the SkillAbilityConfig class in anime game. + */ +public class SkillAbilityInfo { + private List AbilityList; + + public boolean parse(AvatarExcel avatarExcel) { + // Init variable + MazeSkill skill = null; + + // Look for MazeSkill + for (AbilityInfo ability : AbilityList) { + // Skip if not a maze skill + if (!ability.getName().contains("MazeSkill")) { + continue; + } + + // Create maze skill + skill = new MazeSkill(); + + // Parse tasks + for (TaskInfo task : ability.getOnStart()) { + parseTask(skill, skill.getCastActions(), task); + } + } + + // Set skill for avatar + if (skill != null) { + avatarExcel.setMazeSkill(skill); + return true; + } + + return false; + } + + private void parseTask(MazeSkill skill, List actionList, TaskInfo task) { + if (task.getType().contains("AddMazeBuff")) { + actionList.add(new MazeSkillAddBuff(task.getID(), 15)); + } else if (task.getType().contains("CreateSummonUnit")) { + + } else if (task.getSuccessTaskList() != null) { + for (TaskInfo t : task.getSuccessTaskList()) { + parseTask(skill, skill.getCastActions(), t); + } + } else if (task.getOnAttack() != null) { + if (task.getType().contains("AdventureTriggerAttack")) { + for (TaskInfo t : task.getOnAttack()) { + parseTask(skill, skill.getAttackActions(), t); + } + } else if (task.getType().contains("AdventureFireProjectile")) { + for (TaskInfo t : task.getOnAttack()) { + parseTask(skill, skill.getAttackActions(), t); + } + } + } + } + + /** + * The equivalent of the AbilityConfig class in anime game. + */ + @Getter + public class AbilityInfo { + private String Name; + private List OnStart; + } +} diff --git a/src/main/java/emu/lunarcore/data/config/TaskInfo.java b/src/main/java/emu/lunarcore/data/config/TaskInfo.java new file mode 100644 index 0000000..1a10981 --- /dev/null +++ b/src/main/java/emu/lunarcore/data/config/TaskInfo.java @@ -0,0 +1,37 @@ +package emu.lunarcore.data.config; + +import java.util.List; + +import com.google.gson.annotations.SerializedName; + +import lombok.AccessLevel; +import lombok.Getter; + +/** + * The equivalent of the TaskConfig class in anime game. + */ +@Getter +public class TaskInfo { + @Getter(AccessLevel.NONE) + private String $type; + + @SerializedName(value = "ID", alternate = {"SummonUnitID"}) + private int ID; + + private DynamicFloat LifeTime; + + private List OnAttack; + private List SuccessTaskList; + + public String getType() { + return this.$type; + } + + public int getLifeTime() { + if (this.LifeTime == null) { + return 15; // TODO change + } + + return (int) this.LifeTime.getValue(); + } +} diff --git a/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java b/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java index f4503cd..22582f7 100644 --- a/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java +++ b/src/main/java/emu/lunarcore/data/excel/AvatarExcel.java @@ -2,10 +2,13 @@ package emu.lunarcore.data.excel; import java.util.ArrayList; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import emu.lunarcore.data.GameData; import emu.lunarcore.data.GameResource; import emu.lunarcore.data.ResourceType; +import emu.lunarcore.game.battle.skills.MazeSkill; import emu.lunarcore.game.enums.AvatarBaseType; import emu.lunarcore.game.enums.DamageType; import lombok.AccessLevel; @@ -26,11 +29,16 @@ public class AvatarExcel extends GameResource { private int[] RankIDList; private int[] SkillList; + private String JsonPath; @Getter(AccessLevel.NONE) private transient AvatarPromotionExcel[] promotionData; private transient List defaultSkillTrees; + private transient String nameKey; + private transient MazeSkill mazeSkill; private transient int maxSp; + + private static Pattern namePattern = Pattern.compile("(?<=Avatar_)(.*?)(?=_Config)"); public AvatarExcel() { this.defaultSkillTrees = new ArrayList<>(); @@ -40,6 +48,10 @@ public class AvatarExcel extends GameResource { public int getId() { return AvatarID; } + + public void setMazeSkill(MazeSkill skill) { + this.mazeSkill = skill; + } public AvatarPromotionExcel getPromotionData(int i) { return this.promotionData[i]; @@ -60,5 +72,15 @@ public class AvatarExcel extends GameResource { // Cache max sp this.maxSp = (int) this.SPNeed * 100; + + // Get name key + Matcher matcher = namePattern.matcher(this.JsonPath); + + if (matcher.find()) { + this.nameKey = matcher.group(0); + } + + // Clear variable to save memory + this.JsonPath = null; } } diff --git a/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java index 07c1ac8..55d3c22 100644 --- a/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java +++ b/src/main/java/emu/lunarcore/game/avatar/GameAvatar.java @@ -30,8 +30,7 @@ import emu.lunarcore.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.lunarcore.proto.SpBarInfoOuterClass.SpBarInfo; import emu.lunarcore.proto.VectorOuterClass.Vector; import emu.lunarcore.server.packet.send.PacketPlayerSyncScNotify; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.*; import lombok.Getter; import lombok.Setter; @@ -55,11 +54,13 @@ public class GameAvatar implements GameEntity { private transient int entityId; private transient Int2ObjectMap equips; + private transient Int2LongMap buffs; private transient HeroPath heroPath; @Deprecated // Morphia only public GameAvatar() { this.equips = new Int2ObjectOpenHashMap<>(); + this.buffs = Int2LongMaps.synchronize(new Int2LongOpenHashMap()); this.level = 1; this.currentHp = 10000; this.currentSp = 0; @@ -140,6 +141,12 @@ public class GameAvatar implements GameEntity { this.heroPath = heroPath; this.heroPath.setAvatar(this); } + + // Buffs + + public void addBuff(int buffId, int duration) { + this.buffs.put(buffId, System.currentTimeMillis() + (duration * 1000)); + } // Equips diff --git a/src/main/java/emu/lunarcore/game/battle/Battle.java b/src/main/java/emu/lunarcore/game/battle/Battle.java index 3cf01c2..538be9a 100644 --- a/src/main/java/emu/lunarcore/game/battle/Battle.java +++ b/src/main/java/emu/lunarcore/game/battle/Battle.java @@ -27,6 +27,7 @@ public class Battle { private final List npcMonsters; private final List buffs; private final List stages; + private final long timestamp; private Battle(Player player, PlayerLineup lineup) { this.id = player.getNextBattleId(); @@ -35,6 +36,7 @@ public class Battle { this.npcMonsters = new ArrayList<>(); this.buffs = new ArrayList<>(); this.stages = new ArrayList<>(); + this.timestamp = System.currentTimeMillis(); } public Battle(Player player, PlayerLineup lineup, StageExcel stage) { @@ -55,8 +57,9 @@ public class Battle { } } - public MazeBuff addBuff(int buffId, int ownerId) { - return addBuff(buffId, ownerId, 0xffffffff); + + public MazeBuff addBuff(int buffId) { + return addBuff(buffId, 0, 0xffffffff); } public MazeBuff addBuff(int buffId, int ownerId, int waveFlag) { @@ -100,19 +103,30 @@ public class Battle { } } + // Avatars + for (int i = 0; i < lineup.getAvatars().size(); i++) { + GameAvatar avatar = getPlayer().getAvatarById(lineup.getAvatars().get(i)); + if (avatar == null) continue; + + // Add to proto + proto.addBattleAvatarList(avatar.toBattleProto(i)); + + // Add buffs from avatars + if (avatar.getBuffs().size() > 0) { + for (var buffEntry : avatar.getBuffs().int2LongEntrySet()) { + // Check expiry for buff + if (buffEntry.getLongValue() >= this.timestamp) { + this.addBuff(buffEntry.getIntKey()); + } + } + } + } + // Buffs for (MazeBuff buff : this.getBuffs()) { proto.addBuffList(buff.toProto()); } - // Avatars - for (int i = 0; i < lineup.getAvatars().size(); i++) { - GameAvatar avatar = getPlayer().getAvatarById(lineup.getAvatars().get(i)); - if (avatar == null) continue; - - proto.addBattleAvatarList(avatar.toBattleProto(i)); - } - return proto; } } diff --git a/src/main/java/emu/lunarcore/game/battle/BattleService.java b/src/main/java/emu/lunarcore/game/battle/BattleService.java index 363a0b0..fca3ef2 100644 --- a/src/main/java/emu/lunarcore/game/battle/BattleService.java +++ b/src/main/java/emu/lunarcore/game/battle/BattleService.java @@ -7,6 +7,7 @@ import emu.lunarcore.data.GameData; import emu.lunarcore.data.excel.CocoonExcel; import emu.lunarcore.data.excel.StageExcel; import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.skills.MazeSkill; import emu.lunarcore.game.enums.StageType; import emu.lunarcore.game.player.Player; import emu.lunarcore.game.scene.entity.EntityMonster; @@ -31,7 +32,7 @@ public class BattleService extends BaseGameService { super(server); } - public void startBattle(Player player, int attackerId, RepeatedInt attackedList) { + public void startBattle(Player player, int attackerId, MazeSkill castedSkill, RepeatedInt attackedList) { // Sanity check to make sure player isnt in a battle if (player.isInBattle()) { player.sendPacket(new PacketSceneCastSkillScRsp(1)); @@ -107,14 +108,19 @@ public class BattleService extends BaseGameService { Battle battle = new Battle(player, player.getLineupManager().getCurrentLineup(), stages); battle.getNpcMonsters().addAll(monsters); - // Add weakness buff to battle + // Add buffs to battle if (isPlayerCaster) { GameAvatar avatar = player.getLineupManager().getCurrentLeaderAvatar(); if (avatar != null) { - MazeBuff buff = battle.addBuff(avatar.getExcel().getDamageType().getEnterBattleBuff(), 0); + // Add elemental weakness buff to enemies + MazeBuff buff = battle.addBuff(avatar.getExcel().getDamageType().getEnterBattleBuff()); if (buff != null) { buff.addDynamicValue("SkillIndex", 1); } + // Maze skill handlers + if (castedSkill != null) { + castedSkill.onAttack(avatar, battle); + } } } diff --git a/src/main/java/emu/lunarcore/game/battle/skills/MazeSkill.java b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkill.java new file mode 100644 index 0000000..970384b --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkill.java @@ -0,0 +1,38 @@ +package emu.lunarcore.game.battle.skills; + +import java.util.ArrayList; +import java.util.List; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.Battle; +import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; +import lombok.Getter; + +@Getter +public class MazeSkill { + private List castActions; + private List attackActions; + + public MazeSkill() { + this.castActions = new ArrayList<>(); + this.attackActions = new ArrayList<>(); + } + + // Triggered when player casts a skill + public void onCast(GameAvatar caster, MotionInfo castPosition) { + if (this.getCastActions().size() == 0) return; + + for (var action : this.getCastActions()) { + action.onCast(caster, castPosition); + } + } + + // Triggered when player attacks an enemy + public void onAttack(GameAvatar caster, Battle battle) { + if (this.getAttackActions().size() == 0) return; + + for (var action : this.getAttackActions()) { + action.onAttack(caster, battle); + } + } +} diff --git a/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAction.java b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAction.java new file mode 100644 index 0000000..22296c9 --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAction.java @@ -0,0 +1,13 @@ +package emu.lunarcore.game.battle.skills; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.Battle; +import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; + +public abstract class MazeSkillAction { + + public abstract void onCast(GameAvatar caster, MotionInfo castPosition); + + public abstract void onAttack(GameAvatar caster, Battle battle); + +} diff --git a/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAddBuff.java b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAddBuff.java new file mode 100644 index 0000000..a1b700a --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillAddBuff.java @@ -0,0 +1,29 @@ +package emu.lunarcore.game.battle.skills; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.Battle; +import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; +import lombok.AccessLevel; +import lombok.Getter; + +@Getter(AccessLevel.PRIVATE) +public class MazeSkillAddBuff extends MazeSkillAction { + private int buffId; + private int duration; + + public MazeSkillAddBuff(int buffId, int duration) { + this.buffId = buffId; + this.duration = duration; + } + + @Override + public void onCast(GameAvatar caster, MotionInfo castPosition) { + caster.addBuff(buffId, duration); + } + + @Override + public void onAttack(GameAvatar caster, Battle battle) { + battle.addBuff(buffId); + } + +} diff --git a/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillModifySP.java b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillModifySP.java new file mode 100644 index 0000000..58f00ca --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillModifySP.java @@ -0,0 +1,21 @@ +package emu.lunarcore.game.battle.skills; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.Battle; +import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; + +public class MazeSkillModifySP extends MazeSkillAction { + + @Override + public void onCast(GameAvatar caster, MotionInfo castPosition) { + // TODO Auto-generated method stub + + } + + @Override + public void onAttack(GameAvatar caster, Battle battle) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillSummonUnit.java b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillSummonUnit.java new file mode 100644 index 0000000..3339c6d --- /dev/null +++ b/src/main/java/emu/lunarcore/game/battle/skills/MazeSkillSummonUnit.java @@ -0,0 +1,21 @@ +package emu.lunarcore.game.battle.skills; + +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.Battle; +import emu.lunarcore.proto.MotionInfoOuterClass.MotionInfo; + +public class MazeSkillSummonUnit extends MazeSkillAction { + + @Override + public void onCast(GameAvatar caster, MotionInfo castPosition) { + // TODO Auto-generated method stub + + } + + @Override + public void onAttack(GameAvatar caster, Battle battle) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/emu/lunarcore/game/player/LineupManager.java b/src/main/java/emu/lunarcore/game/player/LineupManager.java index e3fc054..d1db5b9 100644 --- a/src/main/java/emu/lunarcore/game/player/LineupManager.java +++ b/src/main/java/emu/lunarcore/game/player/LineupManager.java @@ -127,7 +127,7 @@ public class LineupManager { // No changes were made return false; } - + // Save this.getPlayer().save(); diff --git a/src/main/java/emu/lunarcore/game/player/Player.java b/src/main/java/emu/lunarcore/game/player/Player.java index 5f369e0..38ca44d 100644 --- a/src/main/java/emu/lunarcore/game/player/Player.java +++ b/src/main/java/emu/lunarcore/game/player/Player.java @@ -43,8 +43,6 @@ import lombok.Setter; @Entity(value = "players", useDiscriminator = false) @Getter public class Player { - private transient GameSession session; - @Id private int uid; @Indexed private String accountUid; private String name; @@ -71,18 +69,19 @@ public class Player { private Set unlockedHeadIcons; - // Etc - @Setter private transient boolean paused; - private transient boolean inAnchorRange; - private transient int nextBattleId; - // Player managers + private transient GameSession session; private transient final AvatarStorage avatars; private transient final Inventory inventory; // Database persistent data private LineupManager lineupManager; private PlayerGachaInfo gachaInfo; + + // Etc + @Setter private transient boolean paused; + private transient boolean inAnchorRange; + private transient int nextBattleId; @Deprecated // Morphia only public Player() { diff --git a/src/main/java/emu/lunarcore/game/scene/Scene.java b/src/main/java/emu/lunarcore/game/scene/Scene.java index 3ed7773..f88e406 100644 --- a/src/main/java/emu/lunarcore/game/scene/Scene.java +++ b/src/main/java/emu/lunarcore/game/scene/Scene.java @@ -207,6 +207,9 @@ public class Scene { // Add to entity id cache this.avatarEntityIds.add(avatar.getEntityId()); + + // Clear avatar's buff + avatar.getBuffs().clear(); } for (var avatar : this.avatars.values()) { diff --git a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java index a51b762..6192907 100644 --- a/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java +++ b/src/main/java/emu/lunarcore/server/packet/recv/HandlerSceneCastSkillCsReq.java @@ -1,5 +1,7 @@ package emu.lunarcore.server.packet.recv; +import emu.lunarcore.game.avatar.GameAvatar; +import emu.lunarcore.game.battle.skills.MazeSkill; import emu.lunarcore.proto.SceneCastSkillCsReqOuterClass.SceneCastSkillCsReq; import emu.lunarcore.server.game.GameSession; import emu.lunarcore.server.packet.CmdId; @@ -15,13 +17,23 @@ public class HandlerSceneCastSkillCsReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] data) throws Exception { var req = SceneCastSkillCsReq.parseFrom(data); + MazeSkill castedSkill = null; + + // Check if player casted a maze skill if (req.getSkillIndex() > 0 && session.getPlayer().getScene().getAvatarEntityIds().contains(req.getAttackerId())) { + // Spend one skill point session.getPlayer().getLineupManager().removeMp(1); session.send(new PacketSceneCastSkillMpUpdateScNotify(req.getAttackedGroupId(), session.getPlayer().getLineupManager().getMp())); + // Cast skill effects + GameAvatar caster = session.getPlayer().getLineupManager().getCurrentLeaderAvatar(); + if (caster != null && caster.getExcel().getMazeSkill() != null) { + castedSkill = caster.getExcel().getMazeSkill(); + castedSkill.onCast(caster, req.getTargetMotion()); + } } if (req.hasAttackedEntityIdList()) { - session.getServer().getBattleService().startBattle(session.getPlayer(), req.getAttackerId(), req.getAttackedEntityIdList()); + session.getServer().getBattleService().startBattle(session.getPlayer(), req.getAttackerId(), castedSkill, req.getAttackedEntityIdList()); } else { session.send(new PacketSceneCastSkillScRsp()); }