From 3710f0a697f69f8cacbeb8cf2fe690bcb9cc61ed Mon Sep 17 00:00:00 2001 From: Melledy <121644117+Melledy@users.noreply.github.com> Date: Thu, 4 Dec 2025 19:32:23 -0800 Subject: [PATCH] Implement secondary skills in star tower too --- src/main/java/emu/nebula/GameConstants.java | 6 +- .../data/resources/SecondarySkillDef.java | 57 ++++++ .../emu/nebula/game/tower/StarTowerBuild.java | 87 ++------- .../emu/nebula/game/tower/StarTowerGame.java | 172 +++++++++++++----- .../nebula/game/tower/StarTowerShopGoods.java | 2 +- .../game/tower/cases/StarTowerBattleCase.java | 2 +- .../game/tower/cases/StarTowerHawkerCase.java | 8 +- .../cases/StarTowerStrengthenMachineCase.java | 4 +- .../handlers/HandlerStarTowerApplyReq.java | 2 +- 9 files changed, 207 insertions(+), 133 deletions(-) diff --git a/src/main/java/emu/nebula/GameConstants.java b/src/main/java/emu/nebula/GameConstants.java index e25202f..5a15fe6 100644 --- a/src/main/java/emu/nebula/GameConstants.java +++ b/src/main/java/emu/nebula/GameConstants.java @@ -34,7 +34,6 @@ public class GameConstants { public static final int GEM_ITEM_ID = 2; public static final int PREM_GEM_ITEM_ID = 3; public static final int ENERGY_BUY_ITEM_ID = GEM_ITEM_ID; - public static final int STAR_TOWER_COIN_ITEM_ID = 11; public static final int EXP_ITEM_ID = 21; public static final int MAX_ENERGY = 240; @@ -52,6 +51,11 @@ public class GameConstants { public static final int MAX_FRIENDSHIPS = 50; public static final int MAX_PENDING_FRIENDSHIPS = 30; + public static final int TOWER_COIN_ITEM_ID = 11; + public static final int[] TOWER_COMMON_SUB_NOTE_SKILLS = new int[] { + 90011, 90012, 90013, 90014, 90015, 90016, 90017 + }; + public static int[][] VAMPIRE_SURVIVOR_BONUS_POWER = new int[][] { new int[] {100, 120}, new int[] {200, 150}, diff --git a/src/main/java/emu/nebula/data/resources/SecondarySkillDef.java b/src/main/java/emu/nebula/data/resources/SecondarySkillDef.java index 0f393a3..3eb6fcc 100644 --- a/src/main/java/emu/nebula/data/resources/SecondarySkillDef.java +++ b/src/main/java/emu/nebula/data/resources/SecondarySkillDef.java @@ -4,10 +4,13 @@ import java.util.ArrayList; import java.util.List; import emu.nebula.data.BaseDef; +import emu.nebula.data.GameData; import emu.nebula.data.ResourceType; import emu.nebula.game.inventory.ItemParamMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; @Getter @@ -54,8 +57,62 @@ public class SecondarySkillDef extends BaseDef { // Clear to save memory this.NeedSubNoteSkills = null; } + + // Static sub note skill group group public static List getGroup(int id) { return groups.get(id); } + + public static IntSet calculateSecondarySkills(int[] discIds, ItemParamMap subNotes) { + var secondarySkills = new IntOpenHashSet(); + + // Get first 3 discs + for (int i = 0; i < 3; i++) { + // Disc id + int discId = discIds[i]; + + // Get disc data + var data = GameData.getDiscDataTable().get(discId); + if (data == null) continue; + + // Add secondary skills + int s1= getSecondarySkill(subNotes, data.getSecondarySkillGroupId1()); + if (s1 > 0) { + secondarySkills.add(s1); + } + + int s2 = getSecondarySkill(subNotes, data.getSecondarySkillGroupId2()); + if (s2 > 0) { + secondarySkills.add(s2); + } + } + + return secondarySkills; + } + + private static int getSecondarySkill(ItemParamMap subNotes, int groupId) { + // Check group id + if (groupId <= 0) { + return 0; + } + + // Get group + var group = SecondarySkillDef.getGroup(groupId); + if (group == null) { + return 0; + } + + // Reverse iterator to try and match highest secondary skill first + for (int i = group.size() - 1; i >= 0; i--) { + var data = group.get(i); + + if (data.match(subNotes)) { + return data.getId(); + } + } + + // Failure + return 0; + } } diff --git a/src/main/java/emu/nebula/game/tower/StarTowerBuild.java b/src/main/java/emu/nebula/game/tower/StarTowerBuild.java index 1f70d3a..b1d1349 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerBuild.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerBuild.java @@ -20,8 +20,8 @@ import emu.nebula.proto.PublicStarTower.StarTowerBuildDetail; import emu.nebula.proto.PublicStarTower.StarTowerBuildInfo; import emu.nebula.proto.PublicStarTower.TowerBuildChar; import emu.nebula.util.Snowflake; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; + +import it.unimi.dsi.fastutil.ints.IntSet; import lombok.Getter; @Getter @@ -44,7 +44,7 @@ public class StarTowerBuild implements GameDatabaseObject { private ItemParamMap potentials; private ItemParamMap subNoteSkills; - private IntList secondarySkills; + private IntSet secondarySkills; @Deprecated public StarTowerBuild() { @@ -58,24 +58,15 @@ public class StarTowerBuild implements GameDatabaseObject { this.charPots = new ItemParamMap(); this.potentials = new ItemParamMap(); this.subNoteSkills = new ItemParamMap(); - this.secondarySkills = new IntArrayList(); } public StarTowerBuild(StarTowerGame game) { // Initialize basic variables this(game.getPlayer()); - // Characters - this.charIds = game.getChars().stream() - .filter(c -> c.getId() > 0) - .mapToInt(c -> c.getId()) - .toArray(); - - // Discs - this.discIds = game.getDiscs().stream() - .filter(d -> d.getId() > 0) - .mapToInt(d -> d.getId()) - .toArray(); + // Set char/disc ids + this.charIds = game.getCharIds(); + this.discIds = game.getDiscIds(); // Add potentials for (var entry : game.getPotentials()) { @@ -99,6 +90,9 @@ public class StarTowerBuild implements GameDatabaseObject { this.getSubNoteSkills().put(entry.getIntKey(), entry.getIntValue()); } + // Set secondary skills + this.secondarySkills = game.getSecondarySkills(); + // Caclulate record score and cache it this.calculateScore(); } @@ -134,63 +128,6 @@ public class StarTowerBuild implements GameDatabaseObject { Nebula.getGameDatabase().update(this, this.getUid(), "preference", this.isPreference()); } - // Secondary skills - - private void calculateSecondarySkills() { - // Reset active secondary skills - if (this.secondarySkills == null) { - this.secondarySkills = new IntArrayList(); - } else { - this.secondarySkills.clear(); - } - - // Get first 3 discs - for (int i = 0; i < 3; i++) { - // Disc id - int discId = this.getDiscIds()[i]; - - // Get disc data - var data = GameData.getDiscDataTable().get(discId); - if (data == null) continue; - - // Add secondary skills - int s1= this.getSecondarySkill(data.getSecondarySkillGroupId1()); - if (s1 > 0) { - this.getSecondarySkills().add(s1); - } - - int s2 = this.getSecondarySkill(data.getSecondarySkillGroupId2()); - if (s2 > 0) { - this.getSecondarySkills().add(s2); - } - } - } - - private int getSecondarySkill(int groupId) { - // Check group id - if (groupId <= 0) { - return 0; - } - - // Get group - var group = SecondarySkillDef.getGroup(groupId); - if (group == null) { - return 0; - } - - // Reverse iterator to try and match highest secondary skill first - for (int i = group.size() - 1; i >= 0; i--) { - var data = group.get(i); - - if (data.match(this.getSubNoteSkills())) { - return data.getId(); - } - } - - // Failure - return 0; - } - // Score public int calculateScore() { @@ -210,8 +147,10 @@ public class StarTowerBuild implements GameDatabaseObject { this.score += item.getIntValue() * 15; } - // Calculate secondary skills - this.calculateSecondarySkills(); + // Check secondary skills + if (this.getSecondarySkills() == null) { + this.secondarySkills = SecondarySkillDef.calculateSecondarySkills(this.getDiscIds(), this.getSubNoteSkills()); + } // Add score from secondary skills for (int id : this.getSecondarySkills()) { diff --git a/src/main/java/emu/nebula/game/tower/StarTowerGame.java b/src/main/java/emu/nebula/game/tower/StarTowerGame.java index 5c56a8e..6d635d0 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerGame.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerGame.java @@ -8,8 +8,11 @@ import dev.morphia.annotations.Entity; import emu.nebula.GameConstants; import emu.nebula.data.GameData; +import emu.nebula.data.resources.SecondarySkillDef; import emu.nebula.data.resources.StarTowerDef; import emu.nebula.data.resources.StarTowerStageDef; +import emu.nebula.game.character.GameCharacter; +import emu.nebula.game.character.GameDisc; import emu.nebula.game.formation.Formation; import emu.nebula.game.inventory.ItemParamMap; import emu.nebula.game.player.Player; @@ -34,7 +37,8 @@ import emu.nebula.util.Utils; 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; import lombok.SneakyThrows; import us.hebi.quickbuf.RepeatedMessage; @@ -64,9 +68,8 @@ public class StarTowerGame { private int nextLevelExp; private int charHp; private int battleTime; - private List chars; - private List discs; - private IntList charIds; + private int[] charIds; + private int[] discIds; private int pendingPotentialCases = 0; private int pendingSubNotes = 0; @@ -75,6 +78,7 @@ public class StarTowerGame { private ItemParamMap items; private ItemParamMap res; private ItemParamMap potentials; + private IntSet secondarySkills; // Sub note skill drop list private IntList subNoteDropList; @@ -86,10 +90,6 @@ public class StarTowerGame { private transient StarTowerBuild build; private transient ItemParamMap newInfos; - private static final int[] COMMON_SUB_NOTE_SKILLS = new int[] { - 90011, 90012, 90013, 90014, 90015, 90016, 90017 - }; - @Deprecated // Morphia only public StarTowerGame() { @@ -114,70 +114,75 @@ public class StarTowerGame { this.teamExp = 0; this.nextLevelExp = GameData.getStarTowerTeamExpDataTable().get(2).getNeedExp(); this.charHp = -1; - this.chars = new ArrayList<>(); - this.discs = new ArrayList<>(); - this.charIds = new IntArrayList(); this.items = new ItemParamMap(); this.res = new ItemParamMap(); this.potentials = new ItemParamMap(); + this.secondarySkills = new IntOpenHashSet(); + this.newInfos = new ItemParamMap(); - // Melody skill drop list + // Init melody drop list this.subNoteDropList = new IntArrayList(); // Init modifiers this.modifiers = new StarTowerModifiers(this); // Init formation + IntList charList = new IntArrayList(); + IntList discList = new IntArrayList(); + for (int i = 0; i < 3; i++) { int id = formation.getCharIdAt(i); var character = getPlayer().getCharacters().getCharacterById(id); - if (character != null) { - // Add to character id list - this.charIds.add(id); - - // Add sub note skill id to drop list - int subNoteSkill = character.getData().getElementType().getSubNoteSkillItemId(); - if (subNoteSkill > 0 && !this.subNoteDropList.contains(subNoteSkill)) { - this.subNoteDropList.add(subNoteSkill); - } - - this.chars.add(character.toStarTowerProto()); - } else { - this.chars.add(StarTowerChar.newInstance()); + if (character == null) { + continue; } + + // Add sub note skill id to drop list + int subNoteSkill = character.getData().getElementType().getSubNoteSkillItemId(); + if (subNoteSkill > 0 && !this.subNoteDropList.contains(subNoteSkill)) { + this.subNoteDropList.add(subNoteSkill); + } + + // Add to character list + charList.add(id); } - + for (int i = 0; i < 6; i++) { int id = formation.getDiscIdAt(i); var disc = getPlayer().getCharacters().getDiscById(id); - if (disc != null) { - this.discs.add(disc.toStarTowerProto()); - - // Add star tower sub note skills - if (i >= 3) { - var subNoteData = disc.getSubNoteSkillDef(); - if (subNoteData != null) { - this.getItems().add(subNoteData.getItems()); - } - } - } else { - this.discs.add(StarTowerDisc.newInstance()); + if (disc == null) { + continue; } + + // Add star tower sub note skills from disc + if (i >= 3) { + var subNoteData = disc.getSubNoteSkillDef(); + if (subNoteData != null) { + this.getItems().add(subNoteData.getItems()); + } + } + + // Add to disc list + discList.add(id); } + // Merge char/disc ids into an array for optimization + this.charIds = charList.toIntArray(); + this.discIds = discList.toIntArray(); + // Finish setting up droppable sub note skills - for (int id : COMMON_SUB_NOTE_SKILLS) { + for (int id : GameConstants.TOWER_COMMON_SUB_NOTE_SKILLS) { this.subNoteDropList.add(id); } // Add starting coin directly int coin = this.getModifiers().getStartingCoin(); if (coin > 0) { - this.getRes().add(GameConstants.STAR_TOWER_COIN_ITEM_ID, coin); + this.getRes().add(GameConstants.TOWER_COIN_ITEM_ID, coin); } } @@ -197,6 +202,24 @@ public class StarTowerGame { return this.getData().getDifficulty(); } + public GameCharacter getCharByIndex(int index) { + if (index < 0 || index >= this.getCharIds().length) { + return null; + } + + int id = this.getCharIds()[index]; + return this.getPlayer().getCharacters().getCharacterById(id); + } + + public GameDisc getDiscByIndex(int index) { + if (index < 0 || index >= this.getDiscIds().length) { + return null; + } + + int id = this.getDiscIds()[index]; + return this.getPlayer().getCharacters().getDiscById(id); + } + public int getRandomCharId() { return Utils.randomElement(this.getCharIds()); } @@ -480,7 +503,7 @@ public class StarTowerGame { var list = new IntArrayList(); // Add potentials based on character role - boolean isMainCharacter = this.getCharIds().getInt(0) == charId; + boolean isMainCharacter = this.getCharIds()[0] == charId; if (isMainCharacter) { list.addElements(0, data.getMasterSpecificPotentialIds()); @@ -637,11 +660,11 @@ public class StarTowerGame { rsp.getMutableNilResp(); } - // Add any items + // Add any sub note skills var data = rsp.getMutableData(); if (this.getNewInfos().size() > 0) { - // Add item protos + // Add sub note skills to the proto for (var entry : this.getNewInfos()) { var info = SubNoteSkillInfo.newInstance() .setTid(entry.getIntKey()) @@ -650,11 +673,39 @@ public class StarTowerGame { data.getMutableInfos().add(info); } - // Clear + // Refresh secondary skills + var newSecondarySkills = SecondarySkillDef.calculateSecondarySkills(this.getDiscIds(), this.getItems()); + + // Add any new secondary skills to the data proto + for (int id : newSecondarySkills) { + if (!this.getSecondarySkills().contains(id)) { + var info = ActiveSecondaryChange.newInstance() + .setSecondaryId(id) + .setActive(true); + + data.addSecondaries(info); + } + } + + // Inform the client that these skills are no longer active + for (int id : this.getSecondarySkills()) { + if (!newSecondarySkills.contains(id)) { + var info = ActiveSecondaryChange.newInstance() + .setSecondaryId(id) + .setActive(false); + + data.addSecondaries(info); + } + } + + // Set new secondary skills + this.secondarySkills = newSecondarySkills; + + // Clear new infos this.getNewInfos().clear(); } - // Set these protos + // Always add this proto rsp.getMutableChange(); // Return response proto @@ -687,15 +738,38 @@ public class StarTowerGame { public StarTowerInfo toProto() { var proto = StarTowerInfo.newInstance(); - proto.getMutableMeta() + var meta = proto.getMutableMeta() .setId(this.getId()) .setCharHp(this.getCharHp()) .setTeamLevel(this.getTeamLevel()) - .setNPCInteractions(1) + .setNPCInteractions(0) + .setTotalTime(this.getBattleTime()) .setBuildId(this.getBuildId()); - this.getChars().forEach(proto.getMutableMeta()::addChars); - this.getDiscs().forEach(proto.getMutableMeta()::addDiscs); + // Set characters + discs + for (int i = 0; i < 3; i++) { + var character = this.getCharByIndex(i); + + if (character != null) { + meta.addChars(character.toStarTowerProto()); + } else { + meta.addChars(StarTowerChar.newInstance()); + } + } + for (int i = 0; i < 6; i++) { + var disc = this.getDiscByIndex(i); + + if (disc != null) { + meta.addDiscs(disc.toStarTowerProto()); + } else { + meta.addDiscs(StarTowerDisc.newInstance()); + } + } + + // Add secondary skills + for (int id : this.getSecondarySkills()) { + meta.addActiveSecondaryIds(id); + } // Set room data proto.setRoom(this.getRoom().toProto()); diff --git a/src/main/java/emu/nebula/game/tower/StarTowerShopGoods.java b/src/main/java/emu/nebula/game/tower/StarTowerShopGoods.java index 3a89c87..9ab97fa 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerShopGoods.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerShopGoods.java @@ -55,6 +55,6 @@ public class StarTowerShopGoods { } int index = this.getCharPos() - 1; - return game.getCharIds().getInt(index); + return game.getCharIds()[index]; } } diff --git a/src/main/java/emu/nebula/game/tower/cases/StarTowerBattleCase.java b/src/main/java/emu/nebula/game/tower/cases/StarTowerBattleCase.java index eae950a..a4a724f 100644 --- a/src/main/java/emu/nebula/game/tower/cases/StarTowerBattleCase.java +++ b/src/main/java/emu/nebula/game/tower/cases/StarTowerBattleCase.java @@ -82,7 +82,7 @@ public class StarTowerBattleCase extends StarTowerBaseCase { // Add coin int coin = this.getRoom().getStage().getInteriorCurrencyQuantity(); - this.getGame().addItem(GameConstants.STAR_TOWER_COIN_ITEM_ID, coin, change); + this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, coin, change); // Handle pending potential selectors var nextCases = this.getGame().handlePendingPotentialSelectors(); diff --git a/src/main/java/emu/nebula/game/tower/cases/StarTowerHawkerCase.java b/src/main/java/emu/nebula/game/tower/cases/StarTowerHawkerCase.java index 903572a..027bc23 100644 --- a/src/main/java/emu/nebula/game/tower/cases/StarTowerHawkerCase.java +++ b/src/main/java/emu/nebula/game/tower/cases/StarTowerHawkerCase.java @@ -141,7 +141,7 @@ public class StarTowerHawkerCase extends StarTowerBaseCase { } // Make sure we have enough currency - int coin = this.getGame().getResCount(GameConstants.STAR_TOWER_COIN_ITEM_ID); + int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID); int price = this.getModifiers().getShopRerollPrice(); if (coin < price) { @@ -159,7 +159,7 @@ public class StarTowerHawkerCase extends StarTowerBaseCase { .setHawkerCase(this.toHawkerCaseProto()); // Remove coins - var change = this.getGame().addItem(GameConstants.STAR_TOWER_COIN_ITEM_ID, -price); + var change = this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price); // Set change info rsp.setChange(change.toProto()); @@ -173,7 +173,7 @@ public class StarTowerHawkerCase extends StarTowerBaseCase { } // Make sure we have enough currency - int coin = this.getGame().getResCount(GameConstants.STAR_TOWER_COIN_ITEM_ID); + int coin = this.getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID); int price = goods.getPrice(); if (coin < price || goods.isSold()) { @@ -197,7 +197,7 @@ public class StarTowerHawkerCase extends StarTowerBaseCase { } // Remove coins - this.getGame().addItem(GameConstants.STAR_TOWER_COIN_ITEM_ID, -price, change); + this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price, change); // Set change info rsp.setChange(change.toProto()); diff --git a/src/main/java/emu/nebula/game/tower/cases/StarTowerStrengthenMachineCase.java b/src/main/java/emu/nebula/game/tower/cases/StarTowerStrengthenMachineCase.java index 395ad67..ad7095b 100644 --- a/src/main/java/emu/nebula/game/tower/cases/StarTowerStrengthenMachineCase.java +++ b/src/main/java/emu/nebula/game/tower/cases/StarTowerStrengthenMachineCase.java @@ -50,7 +50,7 @@ public class StarTowerStrengthenMachineCase extends StarTowerBaseCase { StarTowerBaseCase towerCase = null; // Check coin - int coin = getGame().getResCount(GameConstants.STAR_TOWER_COIN_ITEM_ID); + int coin = getGame().getResCount(GameConstants.TOWER_COIN_ITEM_ID); int price = this.getPrice(); if (coin >= price) { @@ -62,7 +62,7 @@ public class StarTowerStrengthenMachineCase extends StarTowerBaseCase { this.getRoom().addCase(rsp.getMutableCases(), towerCase); // Remove coins - var change = this.getGame().addItem(GameConstants.STAR_TOWER_COIN_ITEM_ID, -price); + var change = this.getGame().addItem(GameConstants.TOWER_COIN_ITEM_ID, -price); // Set change info rsp.setChange(change.toProto()); diff --git a/src/main/java/emu/nebula/server/handlers/HandlerStarTowerApplyReq.java b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerApplyReq.java index bd6e97f..b398bc9 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerStarTowerApplyReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerStarTowerApplyReq.java @@ -30,7 +30,7 @@ public class HandlerStarTowerApplyReq extends NetHandler { // Create response var rsp = StarTowerApplyResp.newInstance() .setLastId(req.getId()) - .setCoinQty(game.getResCount(GameConstants.STAR_TOWER_COIN_ITEM_ID)) + .setCoinQty(game.getResCount(GameConstants.TOWER_COIN_ITEM_ID)) .setInfo(game.toProto()) .setChange(change.toProto());