diff --git a/src/main/java/emu/nebula/data/GameData.java b/src/main/java/emu/nebula/data/GameData.java index d31a579..540c6e6 100644 --- a/src/main/java/emu/nebula/data/GameData.java +++ b/src/main/java/emu/nebula/data/GameData.java @@ -50,6 +50,9 @@ public class GameData { @Getter private static DataTable CharGemInstanceDataTable = new DataTable<>(); @Getter private static DataTable WeekBossLevelDataTable = new DataTable<>(); + @Getter private static DataTable GachaDataTable = new DataTable<>(); + @Getter private static DataTable GachaStorageDataTable = new DataTable<>(); + @Getter private static DataTable WorldClassDataTable = new DataTable<>(); @Getter private static DataTable GuideGroupDataTable = new DataTable<>(); @Getter private static DataTable HandbookDataTable = new DataTable<>(); diff --git a/src/main/java/emu/nebula/data/resources/GachaDef.java b/src/main/java/emu/nebula/data/resources/GachaDef.java new file mode 100644 index 0000000..217afec --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/GachaDef.java @@ -0,0 +1,113 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.GameData; +import emu.nebula.data.ResourceType; +import emu.nebula.util.WeightedList; +import lombok.Getter; + +@Getter +@ResourceType(name = "Gacha.json") +public class GachaDef extends BaseDef { + private int Id; + private int StorageId; + private int GachaType; + + private int GuaranteeTimes; + private int GuaranteeTid; + private int GuaranteeQty; + + // Packages + private int ATypePkg; + private int BTypePkg; + private int CTypePkg; + + private int ATypeUpPkg; + private int BTypeUpPkg; + private int CTypeUpPkg; + + private int BGuaranteePkg; + + private transient WeightedList packageA; + private transient WeightedList packageB; + private transient WeightedList packageC; + + @Override + public int getId() { + return Id; + } + + public GachaStorageDef getStorageData() { + return GameData.getGachaStorageDataTable().get(this.getStorageId()); + } + + @Override + public void onLoad() { + // Get storage + var storage = this.getStorageData(); + + // Package A + this.packageA = new WeightedList(); + + if (this.ATypePkg > 0) { + packageA.add( + 10000 - storage.getATypeUpProb(), + new GachaPackage(GachaPackageType.A, this.ATypePkg) + ); + } if (this.ATypeUpPkg > 0) { + packageA.add( + storage.getATypeUpProb(), + new GachaPackage(GachaPackageType.A_UP, this.ATypeUpPkg) + ); + } + + // Package B + this.packageB = new WeightedList(); + + if (this.BTypePkg > 0) { + packageB.add( + 10000 - storage.getBTypeUpProb(), + new GachaPackage(GachaPackageType.B, this.BTypePkg) + ); + } else if (this.BGuaranteePkg > 0) { + packageB.add( + 10000 - storage.getBTypeUpProb(), + new GachaPackage(GachaPackageType.B, this.BGuaranteePkg) + ); + } if (this.BTypeUpPkg > 0) { + packageB.add( + storage.getBTypeUpProb(), + new GachaPackage(GachaPackageType.B_UP, this.BTypeUpPkg) + ); + } + + // Package C + this.packageC = new WeightedList(); + + if (this.CTypePkg > 0) { + packageC.add( + 10000, + new GachaPackage(GachaPackageType.C, this.CTypePkg) + ); + } + } + + @Getter + public static class GachaPackage { + private GachaPackageType type; + private int id; + + public GachaPackage(GachaPackageType type, int id) { + this.type = type; + this.id = id; + } + } + + public enum GachaPackageType { + A, + A_UP, + B, + B_UP, + C; + } +} diff --git a/src/main/java/emu/nebula/data/resources/GachaPkgDef.java b/src/main/java/emu/nebula/data/resources/GachaPkgDef.java new file mode 100644 index 0000000..2989fd2 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/GachaPkgDef.java @@ -0,0 +1,35 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import emu.nebula.data.ResourceType.LoadPriority; +import emu.nebula.util.WeightedList; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import lombok.Getter; + +@Getter +@ResourceType(name = "GachaPkg.json", loadPriority = LoadPriority.LOW) +public class GachaPkgDef extends BaseDef { + private int PkgId; + private int GoodsId; + private int Weight; + + private static final Int2ObjectMap> packages = new Int2ObjectOpenHashMap<>(); + + @Override @Deprecated + public int getId() { + return PkgId; + } + + public static WeightedList getPackageById(int packageId) { + return packages.get(packageId); + } + + @Override + public void onLoad() { + // Add to package + var list = packages.computeIfAbsent(this.getPkgId(), i -> new WeightedList()); + list.add(this.getWeight(), this.getGoodsId()); + } +} diff --git a/src/main/java/emu/nebula/data/resources/GachaStorageDef.java b/src/main/java/emu/nebula/data/resources/GachaStorageDef.java new file mode 100644 index 0000000..31fced5 --- /dev/null +++ b/src/main/java/emu/nebula/data/resources/GachaStorageDef.java @@ -0,0 +1,26 @@ +package emu.nebula.data.resources; + +import emu.nebula.data.BaseDef; +import emu.nebula.data.ResourceType; +import emu.nebula.data.ResourceType.LoadPriority; +import lombok.Getter; + +@Getter +@ResourceType(name = "GachaStorage.json", loadPriority = LoadPriority.HIGH) +public class GachaStorageDef extends BaseDef { + private int Id; + + private int DefaultId; + private int DefaultQty; + private int CostId; + private int CostQty; + + private int ATypeUpProb; + private int BTypeUpProb; + private int BTypeGuaranteeProb; + + @Override + public int getId() { + return Id; + } +} diff --git a/src/main/java/emu/nebula/game/GameContext.java b/src/main/java/emu/nebula/game/GameContext.java index bbdf441..1542bb6 100644 --- a/src/main/java/emu/nebula/game/GameContext.java +++ b/src/main/java/emu/nebula/game/GameContext.java @@ -4,6 +4,7 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; +import emu.nebula.game.gacha.GachaModule; import emu.nebula.game.player.PlayerModule; import emu.nebula.net.GameSession; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; @@ -16,13 +17,16 @@ public class GameContext { // Modules private final PlayerModule playerModule; + private final GachaModule gachaModule; // Cleanup thread private final Timer cleanupTimer; public GameContext() { this.sessions = new Object2ObjectOpenHashMap<>(); + this.playerModule = new PlayerModule(this); + this.gachaModule = new GachaModule(this); this.cleanupTimer = new Timer(); this.cleanupTimer.scheduleAtFixedRate(new CleanupTask(this), 0, TimeUnit.SECONDS.toMillis(60)); diff --git a/src/main/java/emu/nebula/game/character/Character.java b/src/main/java/emu/nebula/game/character/Character.java index b348e16..9aff1dc 100644 --- a/src/main/java/emu/nebula/game/character/Character.java +++ b/src/main/java/emu/nebula/game/character/Character.java @@ -141,7 +141,7 @@ public class Character implements GameDatabaseObject { int exp = 0; // Check if item is an exp item - for (var entry : params.getEntrySet()) { + for (var entry : params.entries()) { var data = GameData.getCharItemExpDataTable().get(entry.getIntKey()); if (data == null) return null; diff --git a/src/main/java/emu/nebula/game/character/GameDisc.java b/src/main/java/emu/nebula/game/character/GameDisc.java index 65fd2ea..70d8409 100644 --- a/src/main/java/emu/nebula/game/character/GameDisc.java +++ b/src/main/java/emu/nebula/game/character/GameDisc.java @@ -130,7 +130,7 @@ public class GameDisc implements GameDatabaseObject { int exp = 0; // Check if item is an exp item - for (var entry : params.getEntrySet()) { + for (var entry : params.entries()) { var data = GameData.getDiscItemExpDataTable().get(entry.getIntKey()); if (data == null) return null; diff --git a/src/main/java/emu/nebula/game/gacha/GachaBannerInfo.java b/src/main/java/emu/nebula/game/gacha/GachaBannerInfo.java new file mode 100644 index 0000000..b46cc44 --- /dev/null +++ b/src/main/java/emu/nebula/game/gacha/GachaBannerInfo.java @@ -0,0 +1,99 @@ +package emu.nebula.game.gacha; + +import org.bson.types.ObjectId; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import emu.nebula.data.resources.GachaDef; +import emu.nebula.data.resources.GachaDef.GachaPackage; +import emu.nebula.data.resources.GachaPkgDef; +import emu.nebula.database.GameDatabaseObject; +import emu.nebula.game.player.Player; +import emu.nebula.util.Utils; +import lombok.Getter; + +@Getter +@Entity(value = "banner_info", useDiscriminator = false) +public class GachaBannerInfo implements GameDatabaseObject { + @Id + private ObjectId id; + + private int bannerId; + private int playerUid; + + private int total; + private int missTimesA; + private int missTimesUpA; + private int missTimesB; + private boolean usedGuarantee; + + @Deprecated //Morphia only + public GachaBannerInfo() { + + } + + public GachaBannerInfo(Player player, GachaDef data) { + this.playerUid = player.getUid(); + this.bannerId = data.getId(); + } + + public int doPull(GachaDef data) { + // Pull chances + int chanceA = 20; // 2% + int chanceB = 100; // 8% + + // 4 star pity + if (this.missTimesB >= 9) { + chanceB = 1000; + } + + // 5 star pity + if (this.missTimesA >= 159) { + chanceA = 1000; + chanceB = 0; + } + + // Add miss times + this.missTimesB++; + this.missTimesA++; + //this.missTimesUpA++; + + // Get random + int random = Utils.randomRange(1, 1000); + GachaPackage gp = null; + + if (random <= chanceA) { + // Reset pity + this.missTimesA = 0; + + // Get A package + gp = data.getPackageA().next(); + } else if (random <= chanceB) { + // Add miss times + this.missTimesB = 0; + + // Get B package + gp = data.getPackageB().next(); + } else { + // Get C package + gp = data.getPackageC().next(); + } + + // Sanity check + if (gp == null) { + return 0; + } + + // Get package + var pkg = GachaPkgDef.getPackageById(gp.getId()); + if (pkg == null) { + return 0; + } + + // Add total pulls + this.total++; + + // Get random id + return pkg.next(); + } +} diff --git a/src/main/java/emu/nebula/game/gacha/GachaManager.java b/src/main/java/emu/nebula/game/gacha/GachaManager.java new file mode 100644 index 0000000..f72a00b --- /dev/null +++ b/src/main/java/emu/nebula/game/gacha/GachaManager.java @@ -0,0 +1,46 @@ +package emu.nebula.game.gacha; + +import java.util.Collection; + +import emu.nebula.Nebula; +import emu.nebula.data.resources.GachaDef; +import emu.nebula.game.player.Player; +import emu.nebula.game.player.PlayerManager; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public class GachaManager extends PlayerManager { + private final Int2ObjectMap bannerInfos; + private boolean loaded; + + public GachaManager(Player player) { + super(player); + this.bannerInfos = new Int2ObjectOpenHashMap<>(); + } + + public synchronized Collection getBannerInfos() { + return this.bannerInfos.values(); + } + + public synchronized GachaBannerInfo getBannerInfo(GachaDef gachaData) { + if (!this.loaded) { + this.loadFromDatabase(); + } + + return this.bannerInfos.computeIfAbsent( + gachaData.getId(), + i -> new GachaBannerInfo(this.getPlayer(), gachaData) + ); + } + + private void loadFromDatabase() { + var db = Nebula.getGameDatabase(); + + db.getObjects(GachaBannerInfo.class, "playerUid", getPlayerUid()).forEach(bannerInfo -> { + this.bannerInfos.put(bannerInfo.getBannerId(), bannerInfo); + }); + + this.loaded = true; + } +} diff --git a/src/main/java/emu/nebula/game/gacha/GachaModule.java b/src/main/java/emu/nebula/game/gacha/GachaModule.java new file mode 100644 index 0000000..b0e8b4a --- /dev/null +++ b/src/main/java/emu/nebula/game/gacha/GachaModule.java @@ -0,0 +1,166 @@ +package emu.nebula.game.gacha; + +import emu.nebula.data.GameData; +import emu.nebula.game.GameContext; +import emu.nebula.game.GameContextModule; +import emu.nebula.game.inventory.ItemAcquireMap; +import emu.nebula.game.inventory.ItemParamMap; +import emu.nebula.game.inventory.ItemType; +import emu.nebula.game.player.Player; +import emu.nebula.game.player.PlayerChangeInfo; +import emu.nebula.proto.Public.Transform; +import it.unimi.dsi.fastutil.ints.IntArrayList; + +public class GachaModule extends GameContextModule { + + public GachaModule(GameContext context) { + super(context); + } + + public GachaResult spin(Player player, int bannerId, int mode) { + // Get pull count + int amount = mode == 2 ? 10 : 1; + + // Get banner data + var banner = GameData.getGachaDataTable().get(bannerId); + if (banner == null) { + return null; + } + + var bannerStorage = banner.getStorageData(); + if (bannerStorage == null) { + return null; + } + + // Create change info + var change = new PlayerChangeInfo(); + + // Check if we have the materials to gacha TODO + int costQty = player.getInventory().getItemCount(bannerStorage.getDefaultId()); + int costReq = bannerStorage.getDefaultQty() * amount; + + if (costReq > costQty) { + // Not enough materials, check if we can convert + int convertQty = player.getInventory().getResourceCount(bannerStorage.getCostId()); + int convertReq = bannerStorage.getCostQty() * (costReq - costQty); + + // Check if we can buy pulls + if (convertReq > convertQty) { + return null; + } + + // Convert to pull currency + player.getInventory().removeItem(bannerStorage.getCostId(), convertReq, change); + } + + // Consume pull currency + player.getInventory().removeItem(bannerStorage.getDefaultId(), Math.min(costReq, costQty), change); + + // Get gacha banner info + var info = player.getGachaManager().getBannerInfo(banner); + + // Do gacha + var results = new IntArrayList(); + + for (int i = 0; i < amount; i++) { + int id = info.doPull(banner); + if (id <= 0) continue; + + results.add(id); + } + + // Setup variables + var acquireItems = new ItemAcquireMap(player, results); + var transformItemsSrc = new ItemParamMap(); + var transformItemsDst = new ItemParamMap(); + var bonusItems = new ItemParamMap(); + + // Add for player + for (var entry : acquireItems.getItems().int2ObjectEntrySet()) { + // Get ids and aquire params + int id = entry.getIntKey(); + var acquire = entry.getValue(); + + // Add to player + if (acquire.getType() == ItemType.Char) { + // Get add amount + int count = acquire.getCount(); + + // Add char to player + if (acquire.getBegin() == 0) { + player.getInventory().addItem(id, 1, change); + count--; + } + + // Talent material + if (count > 0) { + var characterData = GameData.getCharacterDataTable().get(id); + if (characterData == null) continue; + + transformItemsSrc.add(id, count); + transformItemsDst.add(characterData.getFragmentsId(), characterData.getTransformQty() * count); + transformItemsDst.add(24, 40 * count); // Expert permits + } + } else if (acquire.getType() == ItemType.Disc) { + // Get add amount + int begin = acquire.getBegin(); + int count = acquire.getCount(); + + // Add disc to player + if (begin == 0) { + player.getInventory().addItem(id, 1, change); + count--; + begin++; + } + + // Talent material + int maxTransformCount = Math.max(6 - begin, 0); + int transformCount = Math.min(count, maxTransformCount); + int extraCount = count - maxTransformCount; + + // Transform + if (transformCount > 0) { + var discData = GameData.getDiscDataTable().get(id); + if (discData == null) continue; + + // Star material + transformItemsSrc.add(id, transformCount); + transformItemsDst.add(discData.getTransformItemId(), transformCount); + } else if (extraCount > 0) { + // Permit + transformItemsSrc.add(id, extraCount); + transformItemsDst.add(23, 100 * extraCount); + } + + // Add Travel permits + bonusItems.add(23, 100 * acquire.getCount()); + } else { + // Should never happen + bonusItems.add(id, acquire.getCount()); + } + + // Add gold discs + bonusItems.add(602, 30 * acquire.getCount()); + } + + // Add transform items to extra items + bonusItems.add(transformItemsDst); // Add transform items + + // Add extra items + player.getInventory().addItems(bonusItems, change); + + // Add acquire/transform protos + change.add(acquireItems.toProto()); + + var transform = Transform.newInstance(); + transformItemsSrc.toItemTemplateStream().forEach(transform::addSrc); + transformItemsDst.toItemTemplateStream().forEach(transform::addDst); + change.add(transform); + + // Save banner info to database + info.save(); + + // Complete + return new GachaResult(info, change, results); + } +} diff --git a/src/main/java/emu/nebula/game/gacha/GachaResult.java b/src/main/java/emu/nebula/game/gacha/GachaResult.java new file mode 100644 index 0000000..a0fddc2 --- /dev/null +++ b/src/main/java/emu/nebula/game/gacha/GachaResult.java @@ -0,0 +1,19 @@ +package emu.nebula.game.gacha; + +import emu.nebula.game.player.PlayerChangeInfo; +import it.unimi.dsi.fastutil.ints.IntList; +import lombok.Getter; + +@Getter +public class GachaResult { + private GachaBannerInfo info; + private PlayerChangeInfo change; + private IntList cards; + + public GachaResult(GachaBannerInfo info, PlayerChangeInfo change, IntList cards) { + this.info = info; + this.change = change; + this.cards = cards; + } + +} diff --git a/src/main/java/emu/nebula/game/inventory/Inventory.java b/src/main/java/emu/nebula/game/inventory/Inventory.java index 4d94d65..23dfdb4 100644 --- a/src/main/java/emu/nebula/game/inventory/Inventory.java +++ b/src/main/java/emu/nebula/game/inventory/Inventory.java @@ -207,7 +207,7 @@ public class Inventory extends PlayerManager { } // Add items - for (var param : params.getEntrySet()) { + for (var param : params.entries()) { this.addItem(param.getIntKey(), param.getIntValue(), changes); } @@ -242,7 +242,7 @@ public class Inventory extends PlayerManager { } // Remove items - for (var param : params.getEntrySet()) { + for (var param : params.entries()) { this.removeItem(param.getIntKey(), param.getIntValue(), changes); } @@ -293,7 +293,7 @@ public class Inventory extends PlayerManager { public synchronized boolean verifyItems(ItemParamMap params) { boolean hasItems = true; - for (var param : params.getEntrySet()) { + for (var param : params.entries()) { hasItems = this.verifyItem(param.getIntKey(), param.getIntValue()); if (!hasItems) { diff --git a/src/main/java/emu/nebula/game/inventory/ItemAcquireMap.java b/src/main/java/emu/nebula/game/inventory/ItemAcquireMap.java new file mode 100644 index 0000000..c618a84 --- /dev/null +++ b/src/main/java/emu/nebula/game/inventory/ItemAcquireMap.java @@ -0,0 +1,92 @@ +package emu.nebula.game.inventory; + +import emu.nebula.data.GameData; +import emu.nebula.game.player.Player; +import emu.nebula.proto.Public.AcqInfo; +import emu.nebula.proto.Public.Acquire; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntList; + +import lombok.Getter; + +@Getter +public class ItemAcquireMap { + private final Int2ObjectMap items; + + public ItemAcquireMap(Player player, IntList list) { + this.items = new Int2ObjectOpenHashMap<>(); + + for (int id : list) { + // Get item data + var data = GameData.getItemDataTable().get(id); + if (data == null) { + continue; + } + + // Add to acquire map + if (!this.getItems().containsKey(id)) { + // Get starting count + int count = 0; + + // Check item type + if (data.getItemType() == ItemType.Char) { + var character = player.getCharacters().getCharacterById(id); + + if (character != null) { + count = 1; + } + } else if (data.getItemType() == ItemType.Disc) { + var disc = player.getCharacters().getDiscById(id); + + if (disc != null) { + count = 1; + count += disc.getStar(); + count += player.getInventory().getItemCount(disc.getData().getTransformItemId()); + } + } + + var acquireInfo = new ItemAcquireParam(data.getItemType(), count); + acquireInfo.add(); + + this.getItems().put(id, acquireInfo); + } else { + this.getItems().get(id).add(); + } + } + } + + // Proto + + public Acquire toProto() { + var proto = Acquire.newInstance(); + + for (var entry : this.items.int2ObjectEntrySet()) { + var a = AcqInfo.newInstance() + .setTid(entry.getIntKey()) + .setBegin(entry.getValue().getBegin()) + .setCount(entry.getValue().getCount()); + + proto.addList(a); + } + + return proto; + } + + @Getter + public static class ItemAcquireParam { + private ItemType type; + private int begin; + private int count; + + public ItemAcquireParam(ItemType type, int i) { + this.type = type; + this.begin = i; + } + + public void add() { + this.count++; + } + } +} diff --git a/src/main/java/emu/nebula/game/inventory/ItemParamMap.java b/src/main/java/emu/nebula/game/inventory/ItemParamMap.java index 5be0896..f4966e7 100644 --- a/src/main/java/emu/nebula/game/inventory/ItemParamMap.java +++ b/src/main/java/emu/nebula/game/inventory/ItemParamMap.java @@ -7,15 +7,13 @@ import java.util.stream.Stream; import emu.nebula.proto.Public.Item; import emu.nebula.proto.Public.ItemInfo; import emu.nebula.proto.Public.ItemTpl; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; + +import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; + import us.hebi.quickbuf.RepeatedMessage; -public class ItemParamMap extends Int2IntOpenHashMap { +public class ItemParamMap extends Int2IntLinkedOpenHashMap { private static final long serialVersionUID = -4186524272780523459L; - - public FastEntrySet entries() { - return this.int2IntEntrySet(); - } @Override @Deprecated public int addTo(int itemId, int count) { @@ -55,9 +53,7 @@ public class ItemParamMap extends Int2IntOpenHashMap { return params; } - // - - public FastEntrySet getEntrySet() { + public FastEntrySet entries() { return this.int2IntEntrySet(); } @@ -74,13 +70,13 @@ public class ItemParamMap extends Int2IntOpenHashMap { } public Stream toItemTemplateStream() { - return getEntrySet() + return entries() .stream() .map(e -> ItemTpl.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue())); } public Stream toItemProtoStream() { - return getEntrySet() + return entries() .stream() .map(e -> Item.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue())); } diff --git a/src/main/java/emu/nebula/game/player/Player.java b/src/main/java/emu/nebula/game/player/Player.java index 39b3f67..294c9a3 100644 --- a/src/main/java/emu/nebula/game/player/Player.java +++ b/src/main/java/emu/nebula/game/player/Player.java @@ -14,6 +14,7 @@ import emu.nebula.database.GameDatabaseObject; import emu.nebula.game.account.Account; import emu.nebula.game.character.CharacterStorage; import emu.nebula.game.formation.FormationManager; +import emu.nebula.game.gacha.GachaManager; import emu.nebula.game.instance.InstanceManager; import emu.nebula.game.inventory.Inventory; import emu.nebula.game.mail.Mailbox; @@ -66,6 +67,7 @@ public class Player implements GameDatabaseObject { // Managers private final transient CharacterStorage characters; private final transient Inventory inventory; + private transient GachaManager gachaManager; // Referenced data private transient FormationManager formations; @@ -79,6 +81,7 @@ public class Player implements GameDatabaseObject { this.sessions = new HashSet<>(); this.characters = new CharacterStorage(this); this.inventory = new Inventory(this); + this.gachaManager = new GachaManager(this); } public Player(Account account, String name, boolean gender) { diff --git a/src/main/java/emu/nebula/net/NetMsgIdUtils.java b/src/main/java/emu/nebula/net/NetMsgIdUtils.java index 2fc6d12..2634351 100644 --- a/src/main/java/emu/nebula/net/NetMsgIdUtils.java +++ b/src/main/java/emu/nebula/net/NetMsgIdUtils.java @@ -38,7 +38,6 @@ public class NetMsgIdUtils { return msgIdMap.getOrDefault(msgId, "UNKNOWN"); } - @SuppressWarnings("unused") public static void dumpPacketIds() { try (FileWriter writer = new FileWriter("./MsgIds_" + GameConstants.VERSION + ".json")) { // Create sorted tree map diff --git a/src/main/java/emu/nebula/server/handlers/HandlerGachaInformationReq.java b/src/main/java/emu/nebula/server/handlers/HandlerGachaInformationReq.java index b6750b1..5f73b3d 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerGachaInformationReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerGachaInformationReq.java @@ -2,6 +2,7 @@ package emu.nebula.server.handlers; import emu.nebula.net.NetHandler; import emu.nebula.net.NetMsgId; +import emu.nebula.proto.GachaInformation.GachaInfo; import emu.nebula.proto.GachaInformation.GachaInformationResp; import emu.nebula.net.HandlerId; import emu.nebula.net.GameSession; @@ -11,10 +12,23 @@ public class HandlerGachaInformationReq extends NetHandler { @Override public byte[] handle(GameSession session, byte[] message) throws Exception { + // Build response var rsp = GachaInformationResp.newInstance(); - // TODO + for (var bannerInfo : session.getPlayer().getGachaManager().getBannerInfos()) { + var info = GachaInfo.newInstance() + .setId(bannerInfo.getBannerId()) + .setGachaTotalTimes(bannerInfo.getTotal()) + .setTotalTimes(bannerInfo.getTotal()) + .setAupMissTimes(bannerInfo.getMissTimesA()) + .setAMissTimes(bannerInfo.getMissTimesA()) + .setReveFirstTenReward(true) + .setRecvGuaranteeReward(bannerInfo.isUsedGuarantee()); + + rsp.addInformation(info); + } + // Encode and send return session.encodeMsg(NetMsgId.gacha_information_succeed_ack, rsp); } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerGachaNewbieInfoReq.java b/src/main/java/emu/nebula/server/handlers/HandlerGachaNewbieInfoReq.java index 1629d0d..9e3d05a 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerGachaNewbieInfoReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerGachaNewbieInfoReq.java @@ -2,7 +2,8 @@ package emu.nebula.server.handlers; import emu.nebula.net.NetHandler; import emu.nebula.net.NetMsgId; -import emu.nebula.proto.GachaInformation.GachaInformationResp; +import emu.nebula.proto.GachaNewbieInfoOuterClass.GachaNewbieInfo; +import emu.nebula.proto.GachaNewbieInfoOuterClass.GachaNewbieInfoResp; import emu.nebula.net.HandlerId; import emu.nebula.net.GameSession; @@ -11,7 +12,12 @@ public class HandlerGachaNewbieInfoReq extends NetHandler { @Override public byte[] handle(GameSession session, byte[] message) throws Exception { - var rsp = GachaInformationResp.newInstance(); + var rsp = GachaNewbieInfoResp.newInstance(); + var info = GachaNewbieInfo.newInstance() + .setId(5) + .setReceive(true); + + rsp.addList(info); return session.encodeMsg(NetMsgId.gacha_newbie_info_succeed_ack, rsp); } diff --git a/src/main/java/emu/nebula/server/handlers/HandlerGachaSpinReq.java b/src/main/java/emu/nebula/server/handlers/HandlerGachaSpinReq.java index eed1724..4a7bb4c 100644 --- a/src/main/java/emu/nebula/server/handlers/HandlerGachaSpinReq.java +++ b/src/main/java/emu/nebula/server/handlers/HandlerGachaSpinReq.java @@ -2,50 +2,51 @@ package emu.nebula.server.handlers; import emu.nebula.net.NetHandler; import emu.nebula.net.NetMsgId; +import emu.nebula.net.HandlerId; +import emu.nebula.Nebula; +import emu.nebula.net.GameSession; import emu.nebula.proto.GachaSpin.GachaCard; import emu.nebula.proto.GachaSpin.GachaSpinReq; import emu.nebula.proto.GachaSpin.GachaSpinResp; import emu.nebula.proto.Public.ItemTpl; -import emu.nebula.util.Utils; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import emu.nebula.net.HandlerId; -import emu.nebula.Nebula; -import emu.nebula.data.GameData; -import emu.nebula.net.GameSession; @HandlerId(NetMsgId.gacha_spin_req) public class HandlerGachaSpinReq extends NetHandler { - @SuppressWarnings("unused") @Override public byte[] handle(GameSession session, byte[] message) throws Exception { + // Parse request var req = GachaSpinReq.parseFrom(message); - // Temp - var list = new IntArrayList(); + // Do gacha + var result = Nebula.getGameContext().getGachaModule().spin( + session.getPlayer(), + req.getId(), + req.getMode() + ); - for (var def : GameData.getCharacterDataTable()) { - if (def.getGrade() == 1 && def.isAvailable()) { - list.add(def.getId()); - } + if (result == null) { + return session.encodeMsg(NetMsgId.gacha_spin_failed_ack); } // Build response var rsp = GachaSpinResp.newInstance() - .setTime(Nebula.getCurrentTime()); + .setTime(Nebula.getCurrentTime()) + .setAMissTimes(result.getInfo().getMissTimesA()) + .setAupMissTimes(result.getInfo().getMissTimesA()) + .setTotalTimes(result.getInfo().getTotal()) + .setGachaTotalTimes(result.getInfo().getTotal()) + .setAupGuaranteeTimes(result.getInfo().isUsedGuarantee() ? 0 : 1) + .setChange(result.getChange().toProto()); - rsp.getMutableChange(); - rsp.getMutableNextPackage(); - - for (int i = 0; i < 10; i++) { - int id = Utils.randomElement(list); - + for (int id : result.getCards()) { var card = GachaCard.newInstance() .setCard(ItemTpl.newInstance().setTid(id).setQty(1)); rsp.addCards(card); } + // Encode and send response return session.encodeMsg(NetMsgId.gacha_spin_succeed_ack, rsp); } diff --git a/src/main/java/emu/nebula/util/WeightedList.java b/src/main/java/emu/nebula/util/WeightedList.java new file mode 100644 index 0000000..e8bfa75 --- /dev/null +++ b/src/main/java/emu/nebula/util/WeightedList.java @@ -0,0 +1,35 @@ +package emu.nebula.util; + +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.concurrent.ThreadLocalRandom; + +public class WeightedList { + private final NavigableMap map = new TreeMap<>(); + private double total = 0; + + public WeightedList() { + + } + + public WeightedList add(double weight, E result) { + if (weight <= 0) return this; + total += weight; + map.put(total, result); + return this; + } + + public E next() { + double value = ThreadLocalRandom.current().nextDouble() * total; + return map.higherEntry(value).getValue(); + } + + public int size() { + return map.size(); + } + + public void clear() { + this.map.clear(); + this.total = 0; + } +} \ No newline at end of file