Implement cataclysm survivor (basic)

This commit is contained in:
Melledy
2025-11-08 05:57:44 -08:00
parent 2ecc0f28c5
commit c32df9e8aa
10 changed files with 356 additions and 0 deletions

View File

@@ -80,5 +80,10 @@ public class GameData {
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
@Getter private static DataTable<SubNoteSkillPromoteGroupDef> SubNoteSkillPromoteGroupDataTable = new DataTable<>();
// Vampire survivor
@Getter private static DataTable<VampireSurvivorDef> VampireSurvivorDataTable = new DataTable<>();
@Getter private static DataTable<FateCardDef> FateCardDataTable = new DataTable<>();
// Score boss
@Getter private static DataTable<ScoreBossControlDef> ScoreBossControlDataTable = new DataTable<>();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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