mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-14 13:24:43 +01:00
Implement gacha banners (not newbie)
This commit is contained in:
@@ -50,6 +50,9 @@ public class GameData {
|
||||
@Getter private static DataTable<CharGemInstanceDef> CharGemInstanceDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<WeekBossLevelDef> WeekBossLevelDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<GachaDef> GachaDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GachaStorageDef> GachaStorageDataTable = new DataTable<>();
|
||||
|
||||
@Getter private static DataTable<WorldClassDef> WorldClassDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<GuideGroupDef> GuideGroupDataTable = new DataTable<>();
|
||||
@Getter private static DataTable<HandbookDef> HandbookDataTable = new DataTable<>();
|
||||
|
||||
113
src/main/java/emu/nebula/data/resources/GachaDef.java
Normal file
113
src/main/java/emu/nebula/data/resources/GachaDef.java
Normal file
@@ -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<GachaPackage> packageA;
|
||||
private transient WeightedList<GachaPackage> packageB;
|
||||
private transient WeightedList<GachaPackage> 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<GachaPackage>();
|
||||
|
||||
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<GachaPackage>();
|
||||
|
||||
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<GachaPackage>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
35
src/main/java/emu/nebula/data/resources/GachaPkgDef.java
Normal file
35
src/main/java/emu/nebula/data/resources/GachaPkgDef.java
Normal file
@@ -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<WeightedList<Integer>> packages = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
@Override @Deprecated
|
||||
public int getId() {
|
||||
return PkgId;
|
||||
}
|
||||
|
||||
public static WeightedList<Integer> getPackageById(int packageId) {
|
||||
return packages.get(packageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
// Add to package
|
||||
var list = packages.computeIfAbsent(this.getPkgId(), i -> new WeightedList<Integer>());
|
||||
list.add(this.getWeight(), this.getGoodsId());
|
||||
}
|
||||
}
|
||||
26
src/main/java/emu/nebula/data/resources/GachaStorageDef.java
Normal file
26
src/main/java/emu/nebula/data/resources/GachaStorageDef.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
99
src/main/java/emu/nebula/game/gacha/GachaBannerInfo.java
Normal file
99
src/main/java/emu/nebula/game/gacha/GachaBannerInfo.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
46
src/main/java/emu/nebula/game/gacha/GachaManager.java
Normal file
46
src/main/java/emu/nebula/game/gacha/GachaManager.java
Normal file
@@ -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<GachaBannerInfo> bannerInfos;
|
||||
private boolean loaded;
|
||||
|
||||
public GachaManager(Player player) {
|
||||
super(player);
|
||||
this.bannerInfos = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
public synchronized Collection<GachaBannerInfo> 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;
|
||||
}
|
||||
}
|
||||
166
src/main/java/emu/nebula/game/gacha/GachaModule.java
Normal file
166
src/main/java/emu/nebula/game/gacha/GachaModule.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
19
src/main/java/emu/nebula/game/gacha/GachaResult.java
Normal file
19
src/main/java/emu/nebula/game/gacha/GachaResult.java
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
92
src/main/java/emu/nebula/game/inventory/ItemAcquireMap.java
Normal file
92
src/main/java/emu/nebula/game/inventory/ItemAcquireMap.java
Normal file
@@ -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<ItemAcquireParam> 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,16 +7,14 @@ 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) {
|
||||
return this.add(itemId, 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<ItemTpl> toItemTemplateStream() {
|
||||
return getEntrySet()
|
||||
return entries()
|
||||
.stream()
|
||||
.map(e -> ItemTpl.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue()));
|
||||
}
|
||||
|
||||
public Stream<Item> toItemProtoStream() {
|
||||
return getEntrySet()
|
||||
return entries()
|
||||
.stream()
|
||||
.map(e -> Item.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue()));
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
rsp.getMutableChange();
|
||||
rsp.getMutableNextPackage();
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int id = Utils.randomElement(list);
|
||||
.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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
35
src/main/java/emu/nebula/util/WeightedList.java
Normal file
35
src/main/java/emu/nebula/util/WeightedList.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package emu.nebula.util;
|
||||
|
||||
import java.util.NavigableMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class WeightedList<E> {
|
||||
private final NavigableMap<Double, E> map = new TreeMap<>();
|
||||
private double total = 0;
|
||||
|
||||
public WeightedList() {
|
||||
|
||||
}
|
||||
|
||||
public WeightedList<E> 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user