Setup structure for handling avatar techniques

This commit is contained in:
Melledy
2023-10-02 05:47:56 -07:00
parent 4cbb7d92f3
commit 30e6aec86c
17 changed files with 362 additions and 24 deletions

View File

@@ -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");
}
}

View File

@@ -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;
}
}

View File

@@ -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<AbilityInfo> 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<MazeSkillAction> 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<TaskInfo> OnStart;
}
}

View File

@@ -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<TaskInfo> OnAttack;
private List<TaskInfo> SuccessTaskList;
public String getType() {
return this.$type;
}
public int getLifeTime() {
if (this.LifeTime == null) {
return 15; // TODO change
}
return (int) this.LifeTime.getValue();
}
}

View File

@@ -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<AvatarSkillTreeExcel> 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;
}
}

View File

@@ -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<GameItem> 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

View File

@@ -27,6 +27,7 @@ public class Battle {
private final List<EntityMonster> npcMonsters;
private final List<MazeBuff> buffs;
private final List<StageExcel> 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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<MazeSkillAction> castActions;
private List<MazeSkillAction> 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);
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -127,7 +127,7 @@ public class LineupManager {
// No changes were made
return false;
}
// Save
this.getPlayer().save();

View File

@@ -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<Integer> 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() {

View File

@@ -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()) {

View File

@@ -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());
}