Implement level rewards

This commit is contained in:
Melledy
2025-11-07 06:23:42 -08:00
parent b08ad1b7a1
commit 86b2933aa1
6 changed files with 153 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ package emu.nebula.data.resources;
import emu.nebula.data.BaseDef;
import emu.nebula.data.ResourceType;
import emu.nebula.game.inventory.ItemParamMap;
import lombok.Getter;
@Getter
@@ -11,8 +12,19 @@ public class WorldClassDef extends BaseDef {
private int Exp;
private String Reward;
private transient ItemParamMap rewards;
@Override
public int getId() {
return Id;
}
@Override
public void onLoad() {
if (this.Reward != null) {
this.rewards = ItemParamMap.fromJsonString(this.Reward);
} else {
this.rewards = new ItemParamMap();
}
}
}

View File

@@ -23,6 +23,7 @@ import emu.nebula.game.scoreboss.ScoreBossManager;
import emu.nebula.game.story.StoryManager;
import emu.nebula.game.tower.StarTowerManager;
import emu.nebula.net.GameSession;
import emu.nebula.net.NetMsgId;
import emu.nebula.net.NetMsgPacket;
import emu.nebula.proto.PlayerData.DictionaryEntry;
import emu.nebula.proto.PlayerData.DictionaryTab;
@@ -32,6 +33,7 @@ import emu.nebula.proto.Public.NewbieInfo;
import emu.nebula.proto.Public.QuestType;
import emu.nebula.proto.Public.Story;
import emu.nebula.proto.Public.WorldClass;
import emu.nebula.proto.Public.WorldClassRewardState;
import emu.nebula.proto.Public.Title;
import lombok.Getter;
@@ -305,10 +307,15 @@ public class Player implements GameDatabaseObject {
// Check for level ups
while (this.exp >= expRequired && expRequired > 0) {
// Add level
this.level += 1;
this.exp -= expRequired;
// Recalculate exp required
expRequired = this.getMaxExp();
// Set level reward
this.getQuestManager().getLevelRewards().setBit(this.level);
}
// Save to database
@@ -321,6 +328,17 @@ public class Player implements GameDatabaseObject {
this.getExp()
);
// Save level rewards if we changed it
if (oldLevel != this.getLevel()) {
this.getQuestManager().saveLevelRewards();
this.addNextPackage(
NetMsgId.world_class_reward_state_notify,
WorldClassRewardState.newInstance()
.setFlag(getQuestManager().getLevelRewards().toBigEndianByteArray())
);
}
// Calculate changes
var proto = WorldClass.newInstance()
.setAddClass(this.getLevel() - oldLevel)
@@ -514,7 +532,6 @@ public class Player implements GameDatabaseObject {
state.getMutableMail();
state.getMutableBattlePass();
state.getMutableWorldClassReward();
state.getMutableFriendEnergy();
state.getMutableMallPackage();
state.getMutableAchievement();
@@ -571,6 +588,10 @@ public class Player implements GameDatabaseObject {
proto.addDailyActiveIds(id);
}
state.getMutableWorldClassReward()
.setFlag(this.getQuestManager().getLevelRewards().toBigEndianByteArray());
// Add dictionary tabs
for (var dictionaryData : GameData.getDictionaryTabDataTable()) {
var dictionaryProto = DictionaryTab.newInstance()

View File

@@ -1,19 +1,21 @@
package emu.nebula.game.quest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id;
import emu.nebula.Nebula;
import emu.nebula.data.GameData;
import emu.nebula.data.resources.WorldClassDef;
import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.inventory.ItemParamMap;
import emu.nebula.game.player.Player;
import emu.nebula.game.player.PlayerChangeInfo;
import emu.nebula.game.player.PlayerManager;
import emu.nebula.net.NetMsgId;
import emu.nebula.util.Bitset;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
@@ -32,6 +34,9 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
// Quests
private Map<Integer, GameQuest> quests;
// Level rewards
private Bitset levelRewards;
@Deprecated // Morphia only
public QuestManager() {
@@ -42,12 +47,17 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
this.uid = player.getUid();
this.claimedActiveIds = new IntOpenHashSet();
this.quests = new HashMap<>();
this.levelRewards = new Bitset();
this.resetDailyQuests();
this.save();
}
public void saveLevelRewards() {
Nebula.getGameDatabase().update(this, this.getUid(), "levelRewards", this.levelRewards);
}
public synchronized void resetDailyQuests() {
// Reset daily quests
for (var data : GameData.getDailyQuestDataTable()) {
@@ -87,9 +97,9 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
}
}
public PlayerChangeInfo receiveReward(int questId) {
public PlayerChangeInfo receiveQuestReward(int questId) {
// Get received quests
var claimList = new HashSet<GameQuest>();
var claimList = new ArrayList<GameQuest>();
if (questId > 0) {
// Claim specific quest
@@ -140,7 +150,7 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
Nebula.getGameDatabase().update(this, this.getUid(), "activity", this.getActivity());
// Success
return change;
return change.setSuccess(true);
}
public PlayerChangeInfo claimActiveRewards() {
@@ -179,7 +189,54 @@ public class QuestManager extends PlayerManager implements GameDatabaseObject {
Nebula.getGameDatabase().update(this, this.getUid(), "claimedActiveIds", this.getClaimedActiveIds());
// Success
return change;
return change.setSuccess(true);
}
public PlayerChangeInfo receiveWorldClassReward(int id) {
// Get rewards we want to claim
var claimList = new ArrayList<WorldClassDef>();
if (id > 0) {
// Claim specific level reward
if (this.getLevelRewards().isSet(id)) {
var data = GameData.getWorldClassDataTable().get(id);
if (data != null) {
claimList.add(data);
}
}
} else {
// Claim all
for (var data : GameData.getWorldClassDataTable()) {
if (this.getLevelRewards().isSet(data.getId())) {
claimList.add(data);
}
}
}
// Sanity check
if (claimList.isEmpty()) {
return null;
}
// Claim
var rewards = new ItemParamMap();
for (var data : claimList) {
// Add rewards
rewards.add(data.getRewards());
// Unset level rewards
this.getLevelRewards().unsetBit(data.getId());
}
// Add to inventory
var change = this.getPlayer().getInventory().addItems(rewards);
// Save to db
this.saveLevelRewards();
// Success
return change.setSuccess(true);
}
/**

View File

@@ -0,0 +1,28 @@
package emu.nebula.server.handlers;
import emu.nebula.net.NetHandler;
import emu.nebula.net.NetMsgId;
import emu.nebula.proto.PlayerWorldClassRewardReceive.PlayerWorldClassRewardReq;
import emu.nebula.net.HandlerId;
import emu.nebula.net.GameSession;
@HandlerId(NetMsgId.player_world_class_reward_receive_req)
public class HandlerPlayerWorldClassRewardReceiveReq extends NetHandler {
@Override
public byte[] handle(GameSession session, byte[] message) throws Exception {
// Parse request
var req = PlayerWorldClassRewardReq.parseFrom(message);
// Receive rewards
var change = session.getPlayer().getQuestManager().receiveWorldClassReward(req.getClassX());
if (change == null) {
return session.encodeMsg(NetMsgId.player_world_class_reward_receive_failed_ack);
}
// Template
return session.encodeMsg(NetMsgId.player_world_class_reward_receive_succeed_ack, change.toProto());
}
}

View File

@@ -15,7 +15,7 @@ public class HandlerQuestDailyRewardReceiveReq extends NetHandler {
var req = UI32.parseFrom(message);
// Receive rewards
var change = session.getPlayer().getQuestManager().receiveReward(req.getValue());
var change = session.getPlayer().getQuestManager().receiveQuestReward(req.getValue());
if (change == null) {
return session.encodeMsg(NetMsgId.quest_daily_reward_receive_failed_ack);

View File

@@ -39,6 +39,19 @@ public class Bitset {
this.data[longArrayOffset] |= (1L << bytePosition);
}
public void unsetBit(int index) {
int longArrayOffset = (int) Math.floor((index - 1) / 64D);
int bytePosition = ((index - 1) % 64);
if (longArrayOffset >= this.data.length) {
var oldData = this.data;
this.data = new long[longArrayOffset + 1];
System.arraycopy(oldData, 0, this.data, 0, oldData.length);
}
this.data[longArrayOffset] &= (1L << bytePosition);
}
public byte[] toByteArray() {
byte[] array = new byte[this.getData().length * 8];
@@ -53,4 +66,19 @@ public class Bitset {
return array;
}
public byte[] toBigEndianByteArray() {
byte[] array = new byte[this.getData().length * 8];
for (int i = 0; i < this.getData().length; i++) {
long value = this.getData()[i];
for (int x = 0; x <= 7; x++) {
array[(i * 8) + x] = (byte) (value & 0xFF);
value >>= Byte.SIZE;
}
}
return array;
}
}