mirror of
https://github.com/Melledy/Nebula.git
synced 2025-12-16 14:24:57 +01:00
Implement gacha banners (not newbie)
This commit is contained in:
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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user