Implement potential selector in monoliths

This commit is contained in:
Melledy
2025-10-27 08:39:53 -07:00
parent d57caeffe1
commit 08feedc766
10 changed files with 492 additions and 47 deletions

View File

@@ -41,5 +41,6 @@ public class GameData {
@Getter private static DataTable<GuideGroupDef> GuideGroupDataTable = new DataTable<>();
@Getter private static DataTable<StarTowerDef> StarTowerDataTable = new DataTable<>();
@Getter private static DataTable<StarTowerStageDef> StarTowerStageDataTable = new DataTable<>();
@Getter private static DataTable<PotentialDef> PotentialDataTable = new DataTable<>();
}

View File

@@ -14,4 +14,14 @@ public class StarTowerDef extends BaseDef {
public int getId() {
return Id;
}
public int getMaxFloor(int stage) {
int index = stage - 1;
if (index < 0 || index >= this.FloorNum.length) {
return 0;
}
return this.FloorNum[index];
}
}

View File

@@ -0,0 +1,19 @@
package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import lombok.Getter;
@Getter
@ResourceType(name = "StarTowerStage.json")
public class StarTowerStageDef extends BaseDef {
private int Id;
private int Stage;
private int Floor;
private int RoomType;
@Override
public int getId() {
return Id;
}
}

View File

@@ -0,0 +1,87 @@
package emu.nebula.game.tower;
import dev.morphia.annotations.Entity;
import emu.nebula.proto.PublicStarTower.BuildPotential;
import emu.nebula.proto.PublicStarTower.StarTowerBuildInfo;
import emu.nebula.proto.PublicStarTower.TowerBuildChar;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import lombok.Getter;
@Getter
@Entity(useDiscriminator = false)
public class StarTowerBuild {
private int id;
private String name;
private boolean lock;
private boolean preference;
private int score;
private int[] charIds;
private int[] discIds;
private Int2IntMap potentials;
@Deprecated
public StarTowerBuild() {
}
public StarTowerBuild(StarTowerInstance instance) {
this.name = "";
this.potentials = new Int2IntOpenHashMap();
this.charIds = instance.getChars().stream()
.filter(c -> c.getId() > 0)
.mapToInt(c -> c.getId())
.toArray();
this.discIds = instance.getDiscs().stream()
.filter(d -> d.getId() > 0)
.mapToInt(d -> d.getId())
.toArray();
// Add potentials
for (int id : instance.getPotentials()) {
this.getPotentials().put(id, instance.getItemCount(id));
}
}
public void setId(StarTowerManager manager) {
this.id = manager.getNextBuildId();
}
// Proto
public StarTowerBuildInfo toProto() {
var proto = StarTowerBuildInfo.newInstance();
// Basic data
proto.getMutableBrief()
.setId(this.getId())
.setName(this.getName())
.setLock(this.isLock())
.setPreference(this.isPreference())
.setScore(this.getScore())
.addAllDiscIds(this.getDiscIds());
// Add characters
for (int id : charIds) {
var charProto = TowerBuildChar.newInstance()
.setCharId(id);
proto.getMutableBrief().addChars(charProto);
}
// Build detail
var detail = proto.getMutableDetail();
for (var entry : this.getPotentials().int2IntEntrySet()) {
var potential = BuildPotential.newInstance()
.setPotentialId(entry.getIntKey())
.setLevel(entry.getIntValue());
detail.getMutablePotentials().add(potential);
}
return proto;
}
}

View File

@@ -1,6 +1,8 @@
package emu.nebula.game.tower;
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@@ -15,32 +17,59 @@ public class StarTowerCase {
// Extra data
private int teamLevel;
private int subNoteSkillNum;
private int floorId;
private int roomType;
// Select
private int[] ids;
// Selector
private IntList ids;
public StarTowerCase(CaseType type) {
this.type = type;
}
public void addId(int id) {
if (this.ids == null) {
this.ids = new IntArrayList();
}
this.ids.add(id);
}
public int selectId(int index) {
if (this.getIds() == null) {
return 0;
}
if (index < 0 || index >= this.getIds().size()) {
return 0;
}
return this.getIds().getInt(index);
}
public StarTowerRoomCase toProto() {
var proto = StarTowerRoomCase.newInstance()
.setId(this.getId());
switch (this.type) {
case Battle -> {
proto.getMutableBattleCase();
proto.getMutableBattleCase()
.setSubNoteSkillNum(this.getSubNoteSkillNum());
}
case OpenDoor -> {
proto.getMutableDoorCase();
proto.getMutableDoorCase()
.setFloor(this.getFloorId())
.setType(this.getRoomType());
}
case SyncHP -> {
case SyncHP, RecoveryHP -> {
proto.getMutableSyncHPCase();
}
case SelectSpecialPotential -> {
proto.getMutableSelectSpecialPotentialCase();
proto.getMutableSelectSpecialPotentialCase()
.setTeamLevel(this.getTeamLevel())
.addAllIds(this.getIds().toIntArray());
}
case PotentialSelect -> {
proto.getMutableSelectPotentialCase();

View File

@@ -1,18 +1,17 @@
package emu.nebula.game.tower;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import dev.morphia.annotations.Entity;
import emu.nebula.data.GameData;
import emu.nebula.data.resources.PotentialDef;
import emu.nebula.data.resources.StarTowerDef;
import emu.nebula.data.resources.StarTowerStageDef;
import emu.nebula.game.formation.Formation;
import emu.nebula.game.player.Player;
import emu.nebula.proto.PublicStarTower.StarTowerChar;
import emu.nebula.proto.PublicStarTower.StarTowerDisc;
import emu.nebula.proto.PublicStarTower.StarTowerInfo;
import emu.nebula.proto.PublicStarTower.StarTowerRoomCase;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.proto.PublicStarTower.*;
import emu.nebula.proto.StarTowerApply.StarTowerApplyReq;
import emu.nebula.proto.StarTowerInteract.StarTowerInteractReq;
import emu.nebula.proto.StarTowerInteract.StarTowerInteractResp;
@@ -20,6 +19,8 @@ import emu.nebula.util.Snowflake;
import emu.nebula.util.Utils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import lombok.Getter;
import lombok.SneakyThrows;
@@ -33,11 +34,13 @@ public class StarTowerInstance {
private int id;
// Room
private int stage;
private int floor;
private int mapId;
private int mapTableId;
private String mapParam;
private int paramId;
private int roomType;
// Team
private int formationId;
@@ -46,13 +49,19 @@ public class StarTowerInstance {
private int teamExp;
private int charHp;
private int battleTime;
private int battleCount;
private List<StarTowerChar> chars;
private List<StarTowerDisc> discs;
private IntSet charIds;
private int lastCaseId = 0;
private int selectorCaseIndex = -1;
private List<StarTowerCase> cases;
private Int2IntMap items;
private IntSet potentials;
private transient StarTowerBuild build;
@Deprecated // Morphia only
public StarTowerInstance() {
@@ -73,13 +82,16 @@ public class StarTowerInstance {
this.formationId = req.getFormationId();
this.buildId = Snowflake.newUid();
this.teamLevel = 1;
this.stage = 1;
this.floor = 1;
this.charHp = -1;
this.chars = new ArrayList<>();
this.discs = new ArrayList<>();
this.charIds = new IntOpenHashSet();
this.cases = new ArrayList<>();
this.items = new Int2IntOpenHashMap();
this.potentials = new IntOpenHashSet();
// Init formation
for (int i = 0; i < 3; i++) {
@@ -87,9 +99,10 @@ public class StarTowerInstance {
var character = getPlayer().getCharacters().getCharacterById(id);
if (character != null) {
chars.add(character.toStarTowerProto());
this.chars.add(character.toStarTowerProto());
this.charIds.add(id);
} else {
chars.add(StarTowerChar.newInstance());
this.chars.add(StarTowerChar.newInstance());
}
}
@@ -98,9 +111,9 @@ public class StarTowerInstance {
var disc = getPlayer().getCharacters().getDiscById(id);
if (disc != null) {
discs.add(disc.toStarTowerProto());
this.discs.add(disc.toStarTowerProto());
} else {
discs.add(StarTowerDisc.newInstance());
this.discs.add(StarTowerDisc.newInstance());
}
}
@@ -108,23 +121,62 @@ public class StarTowerInstance {
this.addCase(new StarTowerCase(CaseType.Battle));
this.addCase(new StarTowerCase(CaseType.SyncHP));
// Debug
var doorCase = this.addCase(new StarTowerCase(CaseType.OpenDoor));
doorCase.setFloorId(this.getFloor() + 1);
var nextStage = this.getNextStageData();
if (nextStage != null) {
doorCase.setRoomType(nextStage.getRoomType());
}
}
public Player getPlayer() {
return this.manager.getPlayer();
}
public StarTowerBuild getBuild() {
if (this.build == null) {
this.build = new StarTowerBuild(this);
}
return this.build;
}
public StarTowerStageDef getStageData(int stage, int floor) {
var stageId = (this.getId() * 10000) + (stage * 100) + floor;
return GameData.getStarTowerStageDataTable().get(stageId);
}
public StarTowerStageDef getNextStageData() {
int stage = this.stage;
int floor = this.floor + 1;
if (floor >= this.getData().getMaxFloor(this.getStage())) {
floor = 1;
stage++;
}
return getStageData(stage, floor);
}
// Cases
public StarTowerCase getSelectorCase() {
if (this.getSelectorCaseIndex() < 0 || this.getSelectorCaseIndex() >= this.getCases().size()) {
return null;
}
return this.getCases().get(this.getSelectorCaseIndex());
}
public StarTowerCase addCase(StarTowerCase towerCase) {
return this.addCase(null, towerCase);
}
public StarTowerCase addCase(StarTowerInteractResp rsp, StarTowerCase towerCase) {
// Add to cases list
this.cases.add(towerCase);
this.getCases().add(towerCase);
// Increment id
towerCase.setId(++this.lastCaseId);
@@ -134,27 +186,76 @@ public class StarTowerInstance {
rsp.getMutableCases().add(towerCase.toProto());
}
//
if (towerCase.getIds() != null) {
this.selectorCaseIndex = this.getCases().size() - 1;
}
return towerCase;
}
// Items
public int getItemCount(int id) {
return this.getItems().get(id);
}
public PlayerChangeInfo addItem(int id, int count, PlayerChangeInfo changes) {
// Create changes if null
if (changes == null) {
changes = new PlayerChangeInfo();
}
// Get item data
var itemData = GameData.getItemDataTable().get(id);
if (itemData == null) {
return changes;
}
// Add item
this.getItems().put(id, this.getItems().get(id) + count);
// Handle changes
switch (itemData.getItemSubType()) {
case Potential, SpecificPotential -> {
// Add potential
this.getPotentials().add(id);
// Add change
var change = PotentialInfo.newInstance()
.setTid(id)
.setLevel(this.getItems().get(id));
changes.add(change);
}
default -> {
// Ignored
}
}
// Return changes
return changes;
}
// Handlers
public StarTowerInteractResp handleInteract(StarTowerInteractReq req) {
var rsp = StarTowerInteractResp.newInstance()
.setId(req.getId());
if (req.hasBattleEndReq()) {
this.onBattleEnd(req, rsp);
rsp = this.onBattleEnd(req, rsp);
} else if (req.hasRecoveryHPReq()) {
var proto = req.getRecoveryHPReq();
rsp = this.onRecoveryHP(req, rsp);
} else if (req.hasSelectReq()) {
rsp = this.onSelect(req, rsp);
} else if (req.hasEnterReq()) {
this.onEnterReq(req, rsp);
rsp = this.onEnterReq(req, rsp);
}
// Set data protos
rsp.getMutableData();
rsp.getMutableChange();
//rsp.getMutableNextPackage();
return rsp;
}
@@ -162,7 +263,7 @@ public class StarTowerInstance {
// Interact events
@SneakyThrows
public void onBattleEnd(StarTowerInteractReq req, StarTowerInteractResp rsp) {
public StarTowerInteractResp onBattleEnd(StarTowerInteractReq req, StarTowerInteractResp rsp) {
var proto = req.getBattleEndReq();
if (proto.hasVictory()) {
@@ -177,48 +278,132 @@ public class StarTowerInstance {
.getMutableVictory()
.setLv(this.getTeamLevel())
.setBattleTime(this.getBattleTime());
// Add potential selector
var potentialCase = new StarTowerCase(CaseType.SelectSpecialPotential);
potentialCase.setTeamLevel(this.getTeamLevel());
// Add potential selector TODO
// Get random potentials
List<PotentialDef> potentials = new ArrayList<>();
int charId = this.getChars().get(battleCount % this.getCharIds().size()).getId();
for (var potentialData : GameData.getPotentialDataTable()) {
if (potentialData.getCharId() == charId) {
potentials.add(potentialData);
}
}
for (int i = 0; i < 3; i++) {
var potentialData = Utils.randomElement(potentials);
potentialCase.addId(potentialData.getId());
}
// Increment battle count
this.battleCount++;
// Add case
this.addCase(rsp, potentialCase);
} else {
// Handle defeat
// TODO
}
}
public void onSelect(StarTowerInteractReq req, StarTowerInteractResp rsp) {
return rsp;
}
public void onEnterReq(StarTowerInteractReq req, StarTowerInteractResp rsp) {
public StarTowerInteractResp onSelect(StarTowerInteractReq req, StarTowerInteractResp rsp) {
var index = req.getMutableSelectReq().getIndex();
var selectorCase = this.getSelectorCase();
if (selectorCase == null) {
return rsp;
}
int id = selectorCase.selectId(index);
if (id <= 0) {
return rsp;
}
// Add item
var changes = this.addItem(id, 1, null);
rsp.setChange(changes.toProto());
return rsp;
}
public StarTowerInteractResp onEnterReq(StarTowerInteractReq req, StarTowerInteractResp rsp) {
// Get proto
var proto = req.getEnterReq();
// Set
this.floor = this.floor++;
this.mapId = proto.getMapId();
this.mapTableId = proto.getMapTableId();
this.mapParam = proto.getMapParam();
this.paramId = proto.getParamId();
// Clear cases TODO
// Next floor
this.floor++;
if (this.floor >= this.getData().getMaxFloor(this.getStage())) {
this.floor = 1;
this.stage++;
}
// Calculate stage
var stageData = this.getStageData(this.getStage(), this.getFloor());
if (stageData != null) {
this.roomType = stageData.getRoomType();
} else {
this.roomType = 0;
}
// Clear cases
this.selectorCaseIndex = -1;
this.lastCaseId = 0;
this.cases.clear();
// Add cases
var syncHpCase = this.addCase(new StarTowerCase(CaseType.SyncHP));
var doorCase = this.addCase(new StarTowerCase(CaseType.OpenDoor));
var syncHpCase = new StarTowerCase(CaseType.SyncHP);
var doorCase = new StarTowerCase(CaseType.OpenDoor);
doorCase.setFloorId(this.getFloor() + 1);
// Proto
// Set room type of next room
var nextStage = this.getNextStageData();
if (nextStage != null) {
doorCase.setRoomType(nextStage.getRoomType());
}
// Room proto
var room = rsp.getMutableEnterResp().getMutableRoom();
room.setData(this.toRoomDataProto());
room.getMutableData()
.setMapId(this.getMapId())
.setMapTableId(this.getMapTableId())
.setFloor(this.getFloor());
// Handle room type TODO
if (this.roomType <= StarTowerRoomType.EliteBattleRoom.getValue()) {
var battleCase = new StarTowerCase(CaseType.Battle);
battleCase.setSubNoteSkillNum(this.getBattleCount());
this.addCase(battleCase);
room.addCases(battleCase.toProto());
}
room.addAllCases(syncHpCase.toProto(), doorCase.toProto());
// Add cases
this.addCase(syncHpCase);
this.addCase(doorCase);
// Add cases to room
room.addCases(syncHpCase.toProto());
room.addCases(doorCase.toProto());
return rsp;
}
public void onRecoveryHP(StarTowerInteractReq req, StarTowerInteractResp rsp) {
public StarTowerInteractResp onRecoveryHP(StarTowerInteractReq req, StarTowerInteractResp rsp) {
// Add case
this.addCase(rsp, new StarTowerCase(CaseType.RecoveryHP));
return rsp;
}
// Proto
@@ -236,12 +421,7 @@ public class StarTowerInstance {
this.getChars().forEach(proto.getMutableMeta()::addChars);
this.getDiscs().forEach(proto.getMutableMeta()::addDiscs);
proto.getMutableRoom().getMutableData()
.setFloor(this.getFloor())
.setMapId(this.getMapId())
.setMapTableId(this.getMapTableId())
.setMapParam(this.getMapParam())
.setParamId(this.getParamId());
proto.getMutableRoom().setData(this.toRoomDataProto());
// Cases
for (var starTowerCase : this.getCases()) {
@@ -253,4 +433,23 @@ public class StarTowerInstance {
return proto;
}
public StarTowerRoomData toRoomDataProto() {
var proto = StarTowerRoomData.newInstance()
.setFloor(this.getFloor())
.setMapId(this.getMapId())
.setRoomType(this.getRoomType())
.setMapTableId(this.getMapTableId());
if (this.getMapParam() != null && !this.getMapParam().isEmpty()) {
proto.setMapParam(this.getMapParam());
}
if (this.getParamId() != 0) {
proto.setParamId(this.getParamId());
}
return proto;
}
}

View File

@@ -14,6 +14,7 @@ import lombok.Getter;
public class StarTowerManager extends PlayerManager implements GameDatabaseObject {
@Id
private int uid;
private int lastBuildId;
private transient StarTowerInstance instance;
@@ -29,6 +30,10 @@ public class StarTowerManager extends PlayerManager implements GameDatabaseObjec
this.save();
}
public int getNextBuildId() {
return ++this.lastBuildId;
}
public StarTowerInstance apply(StarTowerApplyReq req) {
// Sanity checks
var data = GameData.getStarTowerDataTable().get(req.getId());
@@ -48,4 +53,14 @@ public class StarTowerManager extends PlayerManager implements GameDatabaseObjec
// Success
return this.instance;
}
public StarTowerInstance giveUp() {
var instance = this.instance;
if (instance != null) {
this.instance = null;
}
return instance;
}
}

View File

@@ -0,0 +1,35 @@
package emu.nebula.game.tower;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum StarTowerRoomType {
BattleRoom (0),
EliteBattleRoom (1),
BossRoom (2),
FinalBossRoom (3),
DangerRoom (4),
HorrorRoom (5),
ShopRoom (6),
EventRoom (7),
UnifyBattleRoom (15);
@Getter
private final int value;
private final static Int2ObjectMap<StarTowerRoomType> map = new Int2ObjectOpenHashMap<>();
static {
for (StarTowerRoomType type : StarTowerRoomType.values()) {
map.put(type.getValue(), type);
}
}
private StarTowerRoomType(int value) {
this.value = value;
}
public static StarTowerRoomType getByValue(int value) {
return map.get(value);
}
}

View File

@@ -0,0 +1,20 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.StarTowerBuildWhetherSave.StarTowerBuildWhetherSaveReq;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.star_tower_build_whether_save_req)
public class HandlerStarTowerBuildWhetherSaveReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
var req = StarTowerBuildWhetherSaveReq.parseFrom(message);
// TODO
return this.encodeMsg(NetMsgId.star_tower_build_whether_save_succeed_ack);
}
}

View File

@@ -0,0 +1,30 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.StarTowerGiveUp.StarTowerGiveUpResp;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.star_tower_give_up_req)
public class HandlerStarTowerGiveUpReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
var instance = session.getPlayer().getStarTowerManager().giveUp();
if (instance == null) {
return this.encodeMsg(NetMsgId.star_tower_give_up_failed_ack);
}
// Build response
var rsp = StarTowerGiveUpResp.newInstance()
.setFloor(instance.getFloor());
rsp.getMutableChange();
rsp.setBuild(instance.getBuild().toProto());
return this.encodeMsg(NetMsgId.star_tower_give_up_succeed_ack, rsp);
}
}