From c32df9e8aa36a5c7af0461ec891343e4a4e1c669 Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Sat, 8 Nov 2025 05:57:44 -0800 Subject: [PATCH] Implement cataclysm survivor (basic) --- src/main/java/emu/nebula/data/GameData.java | 5 + .../nebula/data/resources/FateCardDef.java | 21 ++++ .../data/resources/VampireSurvivorDef.java | 16 +++ .../java/emu/nebula/game/player/Player.java | 3 + .../game/vampire/VampireSurvivorGame.java | 99 +++++++++++++++++++ .../game/vampire/VampireSurvivorManager.java | 54 ++++++++++ .../HandlerVampireSurvivorApplyReq.java | 35 +++++++ ...HandlerVampireSurvivorRewardSelectReq.java | 43 ++++++++ .../HandlerVampireSurvivorSettleReq.java | 58 +++++++++++ .../HandlerVampireTalentDetailReq.java | 22 +++++ 10 files changed, 356 insertions(+) create mode 100644 src/main/java/emu/nebula/data/resources/FateCardDef.java create mode 100644 src/main/java/emu/nebula/data/resources/VampireSurvivorDef.java create mode 100644 src/main/java/emu/nebula/game/vampire/VampireSurvivorGame.java create mode 100644 src/main/java/emu/nebula/game/vampire/VampireSurvivorManager.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorApplyReq.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorRewardSelectReq.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorSettleReq.java create mode 100644 src/main/java/emu/nebula/server/handlers/HandlerVampireTalentDetailReq.java diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index 9bbce42..8673e2f 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -80,5 +80,10 @@ public class GameData { @Getter private static DataTable PotentialDataTable = new DataTable<>(); @Getter private static DataTable SubNoteSkillPromoteGroupDataTable = new DataTable<>(); + // Vampire survivor + @Getter private static DataTable VampireSurvivorDataTable = new DataTable<>(); + @Getter private static DataTable FateCardDataTable = new DataTable<>(); + + // Score boss @Getter private static DataTable ScoreBossControlDataTable = new DataTable<>(); } diff --git a/src/main/java/emu/nebula/data/resources/FateCardDef.java b/src/main/java/emu/nebula/data/resources/FateCardDef.java new file mode 100644 index 0000000..1d789fb --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/FateCardDef.java @@ -0,0 +1,21 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "FateCard.json") +public class FateCardDef extends BaseDef { + private int Id; + + private boolean IsTower; + private boolean IsVampire; + private boolean IsVampireSpecial; + private boolean Removable; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/data/resources/VampireSurvivorDef.java b/src/main/java/emu/nebula/data/resources/VampireSurvivorDef.java new file mode 100644 index 0000000..1cf6b40 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/VampireSurvivorDef.java @@ -0,0 +1,16 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import lombok.Getter; + +@Getter +@ResourceType(name = "VampireSurvivor.json") +public class VampireSurvivorDef extends BaseDef { + private int Id; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 4d86aec..9239d0a 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -22,6 +22,7 @@ import emu.nebula.game.quest.QuestManager; import emu.nebula.game.scoreboss.ScoreBossManager; import emu.nebula.game.story.StoryManager; import emu.nebula.game.tower.StarTowerManager; +import emu.nebula.game.vampire.VampireSurvivorManager; import emu.nebula.net.GameSession; import emu.nebula.net.NetMsgId; import emu.nebula.net.NetMsgPacket; @@ -70,6 +71,7 @@ public class Player implements GameDatabaseObject { // Managers private final transient CharacterStorage characters; private final transient GachaManager gachaManager; + private final transient VampireSurvivorManager vampireSurvivorManager; private final transient ScoreBossManager scoreBossManager; // Referenced data @@ -89,6 +91,7 @@ public class Player implements GameDatabaseObject { // Init player managers this.characters = new CharacterStorage(this); this.gachaManager = new GachaManager(this); + this.vampireSurvivorManager = new VampireSurvivorManager(this); this.scoreBossManager = new ScoreBossManager(this); // Init next packages stack diff --git a/src/main/java/emu/nebula/game/vampire/VampireSurvivorGame.java b/src/main/java/emu/nebula/game/vampire/VampireSurvivorGame.java new file mode 100644 index 0000000..95babbd --- /dev/null +++ b/src/main/java/emu/nebula/game/vampire/VampireSurvivorGame.java @@ -0,0 +1,99 @@ +package emu.nebula.game.vampire; + +import emu.nebula.data.GameData; +import emu.nebula.data.resources.VampireSurvivorDef; +import emu.nebula.proto.Public.CardInfo; +import emu.nebula.proto.Public.VampireSurvivorLevelReward; +import emu.nebula.util.WeightedList; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; +import lombok.Getter; + +@Getter +public class VampireSurvivorGame { + private final VampireSurvivorManager manager; + private final VampireSurvivorDef data; + + private IntSet cards; + + private int rewardLevel; + private IntList rewards; + + public VampireSurvivorGame(VampireSurvivorManager manager, VampireSurvivorDef data) { + this.manager = manager; + this.data = data; + + this.cards = new IntOpenHashSet(); + this.rewards = new IntArrayList(); + + this.calcRewards(); + } + + public void calcRewards() { + // Clear reward list first + this.rewards.clear(); + + // Increment level + this.rewardLevel++; + + var random = new WeightedList(); + for (var card : GameData.getFateCardDataTable()) { + // Filter only vampire surv cards + if (!card.isIsVampire()) { + continue; + } + + // Skip cards we already have + if (this.getCards().contains(card.getId())) { + continue; + } + + // Add + random.add(100, card.getId()); + } + + // Add 2 rewards + this.getRewards().add(random.next().intValue()); + this.getRewards().add(random.next().intValue()); + } + + public int selectReward(int index, boolean reRoll) { + // Sanity check + if (index < 0 || index >= this.getRewards().size()) { + return -1; + } + + // Get fate card id + int id = this.getRewards().getInt(index); + + // Add to cards + this.getCards().add(id); + + // Reroll rewards + this.calcRewards(); + + // Success + return id; + } + + // Proto + + public VampireSurvivorLevelReward getRewardProto() { + var proto = VampireSurvivorLevelReward.newInstance() + .setLevel(this.getRewardLevel()); + + var pkg = proto.getMutablePkg(); + + for (int id : this.getRewards()) { + var card = CardInfo.newInstance() + .setId(id); + + pkg.addCards(card); + } + + return proto; + } + +} diff --git a/src/main/java/emu/nebula/game/vampire/VampireSurvivorManager.java b/src/main/java/emu/nebula/game/vampire/VampireSurvivorManager.java new file mode 100644 index 0000000..f72fac4 --- /dev/null +++ b/src/main/java/emu/nebula/game/vampire/VampireSurvivorManager.java @@ -0,0 +1,54 @@ +package emu.nebula.game.vampire; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import emu.nebula.data.GameData; +import emu.nebula.database.GameDatabaseObject; +import emu.nebula.game.player.Player; +import emu.nebula.game.player.PlayerManager; + +import lombok.Getter; + +@Getter +@Entity(value = "vampire", useDiscriminator = false) +public class VampireSurvivorManager extends PlayerManager implements GameDatabaseObject { + @Id + private int uid; + + // Game + private transient VampireSurvivorGame game; + + // TODO talents + + @Deprecated // Morphia only + public VampireSurvivorManager() { + + } + + public VampireSurvivorManager(Player player) { + super(player); + this.uid = player.getUid(); + + //this.save(); + } + + public VampireSurvivorGame apply(int levelId) { + // Get data + var data = GameData.getVampireSurvivorDataTable().get(levelId); + if (data == null) { + return null; + } + + // Create game + this.game = new VampireSurvivorGame(this, data); + + // Success + return this.game; + } + + public void settle(boolean isWin, int score) { + // Clear game + this.game = null; + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorApplyReq.java b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorApplyReq.java new file mode 100644 index 0000000..8b13c6b --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorApplyReq.java @@ -0,0 +1,35 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.VampireSurvivorApply.VampireSurvivorApplyReq; +import emu.nebula.proto.VampireSurvivorApply.VampireSurvivorApplyResp; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.vampire_survivor_apply_req) +public class HandlerVampireSurvivorApplyReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = VampireSurvivorApplyReq.parseFrom(message); + + // Apply + var game = session.getPlayer().getVampireSurvivorManager().apply(req.getId()); + + if (game == null) { + return session.encodeMsg(NetMsgId.vampire_survivor_apply_failed_ack); + } + + // Build response + var rsp = VampireSurvivorApplyResp.newInstance() + .setReward(game.getRewardProto()); + + rsp.getMutableSelect(); + + // Encode and send + return session.encodeMsg(NetMsgId.vampire_survivor_apply_succeed_ack, rsp); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorRewardSelectReq.java b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorRewardSelectReq.java new file mode 100644 index 0000000..9136036 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorRewardSelectReq.java @@ -0,0 +1,43 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.VampireSurvivorRewardSelect.VampireSurvivorRewardSelectReq; +import emu.nebula.proto.VampireSurvivorRewardSelect.VampireSurvivorRewardSelectResp; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.vampire_survivor_reward_select_req) +public class HandlerVampireSurvivorRewardSelectReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse + var req = VampireSurvivorRewardSelectReq.parseFrom(message); + + // Get game + var game = session.getPlayer().getVampireSurvivorManager().getGame(); + + if (game == null) { + session.encodeMsg(NetMsgId.vampire_survivor_reward_select_failed_ack); + } + + // Select + int cardId = game.selectReward(req.getIndex(), req.getReRoll()); + + if (cardId <= 0) { + session.encodeMsg(NetMsgId.vampire_survivor_reward_select_failed_ack); + } + + // Build response + var rsp = VampireSurvivorRewardSelectResp.newInstance(); + + rsp.getMutableResp() + .setFateCardId(cardId) + .setReward(game.getRewardProto()); + + // Encode and send + return session.encodeMsg(NetMsgId.vampire_survivor_reward_select_succeed_ack, rsp); + } + +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorSettleReq.java b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorSettleReq.java new file mode 100644 index 0000000..ba0044b --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerVampireSurvivorSettleReq.java @@ -0,0 +1,58 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.VampireSurvivorSettle.VampireSurvivorAreaInfo; +import emu.nebula.proto.VampireSurvivorSettle.VampireSurvivorSettleReq; +import emu.nebula.proto.VampireSurvivorSettle.VampireSurvivorSettleResp; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.vampire_survivor_settle_req) +public class HandlerVampireSurvivorSettleReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request + var req = VampireSurvivorSettleReq.parseFrom(message); + + // Sanity check + var game = session.getPlayer().getVampireSurvivorManager().getGame(); + + if (game == null) { + session.encodeMsg(NetMsgId.vampire_survivor_settle_failed_ack); + } + + // Calculate victory + score + boolean victory = !req.getDefeat(); + int score = 0; + + // Settle + session.getPlayer().getVampireSurvivorManager().settle(victory, score); + + // Build response + var rsp = VampireSurvivorSettleResp.newInstance(); + + if (victory) { + var areaInfo = VampireSurvivorAreaInfo.newInstance() + .setBossTime(1) + .setScore(score); + + // TODO + for (int i : req.getKillCount()) { + areaInfo.addKillCount(i); + areaInfo.addKillScore(0); + } + + rsp.getMutableVictory() + .setFinalScore(score) + .addInfos(areaInfo); + } else { + rsp.getMutableDefeat() + .setFinalScore(score); + } + + // Encode and send + return session.encodeMsg(NetMsgId.vampire_survivor_settle_succeed_ack, rsp); + } +} diff --git a/src/main/java/emu/nebula/server/handlers/HandlerVampireTalentDetailReq.java b/src/main/java/emu/nebula/server/handlers/HandlerVampireTalentDetailReq.java new file mode 100644 index 0000000..be5f2a7 --- /dev/null +++ b/src/main/java/emu/nebula/server/handlers/HandlerVampireTalentDetailReq.java @@ -0,0 +1,22 @@ +package emu.nebula.server.handlers; + +import emu.nebula.net.NetHandler; +import emu.nebula.net.NetMsgId; +import emu.nebula.proto.VampireTalentDetail.VampireTalentDetailResp; +import emu.nebula.net.HandlerId; +import emu.nebula.net.GameSession; + +@HandlerId(NetMsgId.vampire_talent_detail_req) +public class HandlerVampireTalentDetailReq extends NetHandler { + + @Override + public byte[] handle(GameSession session, byte[] message) throws Exception { + // Build response + var rsp = VampireTalentDetailResp.newInstance() + .setNodes(new byte[8]); + + // Encode and send + return session.encodeMsg(NetMsgId.vampire_talent_detail_succeed_ack, rsp); + } + +}