Initial Commit

This commit is contained in:
Melledy
2025-10-27 02:02:26 -07:00
commit f58951fe2a
378 changed files with 315914 additions and 0 deletions
@@ -0,0 +1,65 @@
package emu.nebula.game.inventory;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.nebula.Nebula;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.Player;
import emu.nebula.proto.Public.Item;
import emu.nebula.util.Utils;
import lombok.Getter;
@Getter
@Entity(value = "items", useDiscriminator = false)
public class GameItem implements GameDatabaseObject {
@Id
private ObjectId uid;
@Indexed
private int playerUid;
private int itemId;
private int count;
@Deprecated
public GameItem() {
}
public GameItem(Player player, int id, int count) {
this.playerUid = player.getUid();
this.itemId = id;
this.count = count;
}
public int add(int amount) {
int oldCount = this.count;
this.count = Utils.safeAdd(this.count, amount, Integer.MAX_VALUE, 0);
return this.count - oldCount;
}
// Database
@Override
public void save() {
if (this.getCount() <= 0) {
if (this.getUid() != null) {
Nebula.getGameDatabase().delete(this);
}
} else {
Nebula.getGameDatabase().save(this);
}
}
// Proto
public Item toProto() {
var proto = Item.newInstance()
.setTid(this.getItemId())
.setQty(this.getCount());
return proto;
}
}
@@ -0,0 +1,63 @@
package emu.nebula.game.inventory;
import org.bson.types.ObjectId;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed;
import emu.nebula.Nebula;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.player.Player;
import emu.nebula.proto.Public.Res;
import emu.nebula.util.Utils;
import lombok.Getter;
@Getter
@Entity(value = "resources", useDiscriminator = false)
public class GameResource implements GameDatabaseObject {
@Id
private ObjectId uid;
@Indexed
private int playerUid;
public int resourceId;
public int count;
@Deprecated // Morphia only
public GameResource() {
}
public GameResource(Player player, int id, int count) {
this.playerUid = player.getUid();
this.resourceId = id;
this.count = count;
}
public int add(int amount) {
int oldCount = this.count;
this.count = Utils.safeAdd(this.count, amount, Integer.MAX_VALUE, 0);
return this.count - oldCount;
}
@Override
public void save() {
if (this.getCount() <= 0) {
if (this.getUid() != null) {
Nebula.getGameDatabase().delete(this);
}
} else {
Nebula.getGameDatabase().save(this);
}
}
// Proto
public Res toProto() {
var proto = Res.newInstance()
.setTid(this.getResourceId())
.setQty(this.getCount());
return proto;
}
}
@@ -0,0 +1,310 @@
package emu.nebula.game.inventory;
import java.util.List;
import emu.nebula.Nebula;
import emu.nebula.data.GameData;
import emu.nebula.game.player.PlayerManager;
import emu.nebula.proto.Public.Item;
import emu.nebula.proto.Public.Res;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@Getter
public class Inventory extends PlayerManager {
private final Int2ObjectMap<GameResource> resources;
private final Int2ObjectMap<GameItem> items;
public Inventory(Player player) {
super(player);
this.resources = new Int2ObjectOpenHashMap<>();
this.items = new Int2ObjectOpenHashMap<>();
}
// Resources
public synchronized int getResourceCount(int id) {
var res = this.resources.get(id);
return res != null ? res.getCount() : 0;
}
// Items
public synchronized int getItemCount(int id) {
var item = this.getItems().get(id);
return item != null ? item.getCount() : 0;
}
//
public synchronized PlayerChangeInfo addItem(int id, int count, PlayerChangeInfo changes) {
// Changes
if (changes == null) {
changes = new PlayerChangeInfo();
}
// Sanity
if (count == 0) {
return changes;
}
// Get game data
var data = GameData.getItemDataTable().get(id);
if (data == null) {
return changes;
}
// Set amount
int amount = count;
// Add item
switch (data.getItemType()) {
case Res -> {
var res = this.resources.get(id);
int diff = 0;
if (amount > 0) {
// Add resource
if (res == null) {
res = new GameResource(this.getPlayer(), id, amount);
this.resources.put(res.getResourceId(), res);
diff = amount;
} else {
diff = res.add(amount);
}
res.save();
} else {
// Remove resource
if (res == null) {
break;
}
diff = res.add(amount);
res.save();
if (res.getCount() < 0) {
this.resources.remove(id);
}
}
if (diff != 0) {
var change = Res.newInstance()
.setTid(id)
.setQty(diff);
changes.add(change);
}
}
case Item -> {
var item = this.items.get(id);
int diff = 0;
if (amount > 0) {
// Add resource
if (item == null) {
item = new GameItem(this.getPlayer(), id, amount);
this.items.put(item.getItemId(), item);
diff = amount;
} else {
diff = item.add(amount);
}
item.save();
} else {
// Remove resource
if (item == null) {
break;
}
diff = item.add(amount);
item.save();
if (item.getCount() < 0) {
this.resources.remove(id);
}
}
if (diff != 0) {
var change = Item.newInstance()
.setTid(id)
.setQty(diff);
changes.add(change);
}
}
case Disc -> {
if (amount <= 0) {
break;
}
var disc = getPlayer().getCharacters().addDisc(id);
if (disc != null) {
changes.add(disc.toProto());
}
}
case Char -> {
if (amount <= 0) {
break;
}
var character = getPlayer().getCharacters().addCharacter(id);
if (character != null) {
changes.add(character.toProto());
}
}
case WorldRankExp -> {
this.getPlayer().addExp(amount, changes);
}
default -> {
// Not implemented
}
}
//
return changes;
}
@Deprecated
public synchronized PlayerChangeInfo addItems(List<ItemParam> params, PlayerChangeInfo changes) {
// Changes
if (changes == null) {
changes = new PlayerChangeInfo();
}
for (ItemParam param : params) {
this.addItem(param.getId(), param.getCount(), changes);
}
return changes;
}
public synchronized PlayerChangeInfo addItems(ItemParamMap params) {
return this.addItems(params, null);
}
public synchronized PlayerChangeInfo addItems(ItemParamMap params, PlayerChangeInfo changes) {
// Changes
if (changes == null) {
changes = new PlayerChangeInfo();
}
for (var param : params.getEntrySet()) {
this.addItem(param.getIntKey(), param.getIntValue(), changes);
}
return changes;
}
public synchronized PlayerChangeInfo removeItem(int id, int count, PlayerChangeInfo changes) {
if (count > 0) {
count = -count;
}
return this.addItem(id, count, changes);
}
public synchronized PlayerChangeInfo removeItems(ItemParamMap params) {
return this.removeItems(params, null);
}
public synchronized PlayerChangeInfo removeItems(ItemParamMap params, PlayerChangeInfo changes) {
// Changes
if (changes == null) {
changes = new PlayerChangeInfo();
}
for (var param : params.getEntrySet()) {
this.removeItem(param.getIntKey(), param.getIntValue(), changes);
}
return changes;
}
/**
* Checks if the player has enough quanity of this item
*/
public synchronized boolean verifyItem(int id, int count) {
// Sanity check
if (count == 0) {
return true;
} else if (count < 0) {
// Return false if we are trying to verify negative numbers
return false;
}
// Get game data
var data = GameData.getItemDataTable().get(id);
if (data == null) {
return false;
}
boolean result = switch (data.getItemType()) {
case Res -> {
yield this.getResourceCount(id) >= count;
}
case Item -> {
yield this.getItemCount(id) >= count;
}
case Disc -> {
yield getPlayer().getCharacters().hasDisc(id);
}
case Char -> {
yield getPlayer().getCharacters().hasCharacter(id);
}
default -> {
// Not implemented
yield false;
}
};
//
return result;
}
public synchronized boolean verifyItems(ItemParamMap params) {
boolean hasItems = true;
for (var param : params.getEntrySet()) {
hasItems = this.verifyItem(param.getIntKey(), param.getIntValue());
if (!hasItems) {
return hasItems;
}
}
return hasItems;
}
// Database
public void loadFromDatabase() {
var db = Nebula.getGameDatabase();
db.getObjects(GameItem.class, "playerUid", getPlayerUid()).forEach(item -> {
// Get data
var data = GameData.getItemDataTable().get(item.getItemId());
if (data == null) return;
// Add
this.items.put(item.getItemId(), item);
});
db.getObjects(GameResource.class, "playerUid", getPlayerUid()).forEach(res -> {
// Get data
var data = GameData.getItemDataTable().get(res.getResourceId());
if (data == null) return;
// Add
this.resources.put(res.getResourceId(), res);
});
}
}
@@ -0,0 +1,30 @@
package emu.nebula.game.inventory;
import dev.morphia.annotations.Entity;
import emu.nebula.proto.Public.ItemTpl;
import lombok.Getter;
@Getter
@Entity(useDiscriminator = false)
public class ItemParam {
public int id;
public int count;
@Deprecated // Morphia only
public ItemParam() {
}
public ItemParam(int id, int count) {
this.id = id;
this.count = count;
}
public ItemTpl toProto() {
var proto = ItemTpl.newInstance()
.setTid(this.getId())
.setQty(this.getCount());
return proto;
}
}
@@ -0,0 +1,100 @@
package emu.nebula.game.inventory;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import emu.nebula.proto.Public.ItemInfo;
import emu.nebula.proto.Public.ItemTpl;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import us.hebi.quickbuf.RepeatedMessage;
public class ItemParamMap extends Int2IntOpenHashMap {
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);
}
public int add(int itemId, int count) {
if (count == 0) {
return 0;
}
return super.addTo(itemId, count);
}
/**
* Adds all item params from the other map to this one
* @param map The other item param map
*/
public void add(ItemParamMap map) {
for (var entry : map.entries()) {
this.add(entry.getIntKey(), entry.getIntValue());
}
}
/**
* Returns a new ItemParamMap with item amounts multiplied
* @param mult Value to multiply all item amounts in this map by
* @return
*/
public ItemParamMap mulitply(int multiplier) {
var params = new ItemParamMap();
for (var entry : this.int2IntEntrySet()) {
params.put(entry.getIntKey(), entry.getIntValue() * multiplier);
}
return params;
}
//
public FastEntrySet getEntrySet() {
return this.int2IntEntrySet();
}
public List<ItemParam> toList() {
List<ItemParam> list = new ArrayList<>();
for (var entry : this.int2IntEntrySet()) {
list.add(new ItemParam(entry.getIntKey(), entry.getIntValue()));
}
return list;
}
public Stream<ItemTpl> itemTemplateStream() {
return getEntrySet()
.stream()
.map(e -> ItemTpl.newInstance().setTid(e.getIntKey()).setQty(e.getIntValue()));
}
// Proto
public static ItemParamMap fromTemplates(RepeatedMessage<ItemTpl> items) {
var map = new ItemParamMap();
for (var template : items) {
map.add(template.getTid(), template.getQty());
}
return map;
}
public static ItemParamMap fromItemInfos(RepeatedMessage<ItemInfo> items) {
var map = new ItemParamMap();
for (var template : items) {
map.add(template.getTid(), template.getQty());
}
return map;
}
}
@@ -0,0 +1,56 @@
package emu.nebula.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum ItemSubType {
Res (1),
Item (2),
Char (3),
Energy (4),
WorldRankExp (5),
CharShard (6),
Disc (8),
TalentStrengthen (9),
DiscStrengthen (12),
DiscPromote (13),
TreasureBox (17),
GearTreasureBox (18),
SubNoteSkill (19),
SkillStrengthen (24),
CharacterLimitBreak (25),
MonthlyCard (30),
EnergyItem (31),
ComCYO (32),
OutfitCYO (33),
RandomPackage (34),
Equipment (35),
FateCard (37),
EquipmentExp (38),
DiscLimitBreak (40),
Potential (41),
SpecificPotential (42),
Honor (43),
CharacterYO (44),
PlayHead (45),
CharacterSkin (46);
@Getter
private final int value;
private final static Int2ObjectMap<ItemSubType> map = new Int2ObjectOpenHashMap<>();
static {
for (ItemSubType type : ItemSubType.values()) {
map.put(type.getValue(), type);
}
}
private ItemSubType(int value) {
this.value = value;
}
public static ItemSubType getByValue(int value) {
return map.get(value);
}
}
@@ -0,0 +1,39 @@
package emu.nebula.game.inventory;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum ItemType {
Res (1),
Item (2),
Char (3),
Energy (4),
WorldRankExp (5),
RogueItem (6),
Disc (7),
Equipment (8),
CharacterSkin (9),
MonthlyCard (10),
Title (11),
Honor (12),
HeadItem (13);
@Getter
private final int value;
private final static Int2ObjectMap<ItemType> map = new Int2ObjectOpenHashMap<>();
static {
for (ItemType type : ItemType.values()) {
map.put(type.getValue(), type);
}
}
private ItemType(int value) {
this.value = value;
}
public static ItemType getByValue(int value) {
return map.get(value);
}
}