mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-27 03:59:16 +00:00
Implement Resin (#1257)
* Basic resin usage/refresh. * Honor resin config, move some logic to logon. * Add resin usage to DungeonChallenge * Make fragile and transient resin usable. * Get resin cost from dungeon excel. * Add ability to unlock combine diagrams. * Refactor CombineManager to use Inventory.payItems, enabling crafting of condensed resin. * Refactor ForgingManager to use Inventory.payItems, to prepare for eventually forging Mystic Enhancement Ores using resin. * Remove comment * Check resin usage in addResin
This commit is contained in:
parent
2560b14557
commit
ded7ce1dcf
@ -16,6 +16,9 @@ public class DungeonData extends GameResource {
|
|||||||
|
|
||||||
private RewardPreviewData previewData;
|
private RewardPreviewData previewData;
|
||||||
|
|
||||||
|
private int statueCostID;
|
||||||
|
private int statueCostCount;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return this.id;
|
return this.id;
|
||||||
@ -33,6 +36,14 @@ public class DungeonData extends GameResource {
|
|||||||
return previewData;
|
return previewData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getStatueCostID() {
|
||||||
|
return statueCostID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatueCostCount() {
|
||||||
|
return statueCostCount;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
if (this.passRewardPreviewID > 0) {
|
if (this.passRewardPreviewID > 0) {
|
||||||
|
@ -3,13 +3,17 @@ package emu.grasscutter.game.combine;
|
|||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.common.ItemParamData;
|
import emu.grasscutter.data.common.ItemParamData;
|
||||||
import emu.grasscutter.data.excels.CombineData;
|
import emu.grasscutter.data.excels.CombineData;
|
||||||
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||||
import emu.grasscutter.server.game.GameServer;
|
import emu.grasscutter.server.game.GameServer;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketCombineRsp;
|
import emu.grasscutter.server.packet.send.PacketCombineRsp;
|
||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CombineManger {
|
public class CombineManger {
|
||||||
@ -23,6 +27,27 @@ public class CombineManger {
|
|||||||
this.gameServer = gameServer;
|
this.gameServer = gameServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean unlockCombineDiagram(Player player, GameItem diagramItem) {
|
||||||
|
// Make sure this is actually a diagram.
|
||||||
|
if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the combine item we should unlock.
|
||||||
|
int combineId = Integer.parseInt(diagramItem.getItemData().getItemUse().get(0).getUseParam().get(0));
|
||||||
|
|
||||||
|
// Remove the diagram from the player's inventory.
|
||||||
|
// We need to do this here, before sending CombineFormulaDataNotify, or the the combine UI won't correctly
|
||||||
|
// update when unlocking the diagram.
|
||||||
|
player.getInventory().removeItem(diagramItem, 1);
|
||||||
|
|
||||||
|
// Tell the client that this diagram is now unlocked and add the unlocked item to the player.
|
||||||
|
player.getUnlockedCombines().add(combineId);
|
||||||
|
player.sendPacket(new PacketCombineFormulaDataNotify(combineId));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public CombineResult combineItem(Player player, int cid, int count){
|
public CombineResult combineItem(Player player, int cid, int count){
|
||||||
// check config exist
|
// check config exist
|
||||||
if(!GameData.getCombineDataMap().containsKey(cid)){
|
if(!GameData.getCombineDataMap().containsKey(cid)){
|
||||||
@ -35,36 +60,17 @@ public class CombineManger {
|
|||||||
if(combineData.getPlayerLevel() > player.getLevel()){
|
if(combineData.getPlayerLevel() > player.getLevel()){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// check enough
|
|
||||||
var enough = combineData.getMaterialItems().stream()
|
|
||||||
.filter(item -> player.getInventory()
|
|
||||||
.getInventoryTab(ItemType.ITEM_MATERIAL)
|
|
||||||
.getItemById(item.getId())
|
|
||||||
.getCount() < item.getCount() * count
|
|
||||||
)
|
|
||||||
.findAny()
|
|
||||||
.isEmpty();
|
|
||||||
|
|
||||||
// if not enough
|
// consume items
|
||||||
if(!enough){
|
List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems());
|
||||||
player.getWorld().getHost().sendPacket(
|
material.add(new ItemParamData(202, combineData.getScoinCost()));
|
||||||
new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE)
|
|
||||||
);
|
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), count, ActionReason.Combine);
|
||||||
return null;
|
|
||||||
|
// abort if not enough material
|
||||||
|
if (!success) {
|
||||||
|
player.sendPacket(new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE));
|
||||||
}
|
}
|
||||||
if (player.getMora() >= combineData.getScoinCost()) {
|
|
||||||
player.setMora(player.getMora() - combineData.getScoinCost() * count);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// try to remove materials
|
|
||||||
combineData.getMaterialItems().stream()
|
|
||||||
.map(item -> Pair.of(player.getInventory()
|
|
||||||
.getInventoryTab(ItemType.ITEM_MATERIAL)
|
|
||||||
.getItemById(item.getId())
|
|
||||||
,item.getCount() * count)
|
|
||||||
)
|
|
||||||
.forEach(item -> player.getInventory().removeItem(item.first(), item.second()));
|
|
||||||
|
|
||||||
// make the result
|
// make the result
|
||||||
player.getInventory().addItem(combineData.getResultItemId(),
|
player.getInventory().addItem(combineData.getResultItemId(),
|
||||||
|
@ -4,9 +4,11 @@ import emu.grasscutter.data.common.ItemParamData;
|
|||||||
import emu.grasscutter.data.excels.DungeonData;
|
import emu.grasscutter.data.excels.DungeonData;
|
||||||
import emu.grasscutter.game.entity.EntityMonster;
|
import emu.grasscutter.game.entity.EntityMonster;
|
||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.game.world.Scene;
|
import emu.grasscutter.game.world.Scene;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.scripts.constants.EventType;
|
import emu.grasscutter.scripts.constants.EventType;
|
||||||
import emu.grasscutter.scripts.data.SceneGroup;
|
import emu.grasscutter.scripts.data.SceneGroup;
|
||||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||||
@ -153,8 +155,20 @@ public class DungeonChallenge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getStatueDrops(Player player) {
|
private List<GameItem> rollRewards() {
|
||||||
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
|
|
||||||
|
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||||
|
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||||
DungeonData dungeonData = getScene().getDungeonData();
|
DungeonData dungeonData = getScene().getDungeonData();
|
||||||
|
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||||
|
|
||||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -164,11 +178,43 @@ public class DungeonChallenge {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get rewards.
|
||||||
List<GameItem> rewards = new ArrayList<>();
|
List<GameItem> rewards = new ArrayList<>();
|
||||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
|
||||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
if (request.getIsUseCondenseResin()) {
|
||||||
|
// Check if condensed resin is usable here.
|
||||||
|
// For this, we use the following logic for now:
|
||||||
|
// The normal resin cost of the dungeon has to be 20.
|
||||||
|
if (resinCost != 20) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the player has condensed resin.
|
||||||
|
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
||||||
|
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct.
|
||||||
|
player.getInventory().removeItem(condensedResin, 1);
|
||||||
|
|
||||||
|
// Roll rewards, twice (because condensed).
|
||||||
|
rewards.addAll(this.rollRewards());
|
||||||
|
rewards.addAll(this.rollRewards());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If the player used regular resin, try to deduct.
|
||||||
|
// Stop if insufficient resin.
|
||||||
|
boolean success = player.getResinManager().useResin(resinCost);
|
||||||
|
if (!success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Roll rewards.
|
||||||
|
rewards.addAll(this.rollRewards());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add rewards to player and send notification.
|
||||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import emu.grasscutter.game.avatar.AvatarStorage;
|
|||||||
import emu.grasscutter.game.avatar.Avatar;
|
import emu.grasscutter.game.avatar.Avatar;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
import emu.grasscutter.game.props.ActionReason;
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||||
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
||||||
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
||||||
@ -258,6 +259,8 @@ public class Inventory implements Iterable<GameItem> {
|
|||||||
getPlayer().addExpDirectly(count);
|
getPlayer().addExpDirectly(count);
|
||||||
case 105 -> // Companionship exp
|
case 105 -> // Companionship exp
|
||||||
getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
|
getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
|
||||||
|
case 106 -> // Resin
|
||||||
|
getPlayer().getResinManager().addResin(count);
|
||||||
case 201 -> // Primogem
|
case 201 -> // Primogem
|
||||||
getPlayer().setPrimogems(player.getPrimogems() + count);
|
getPlayer().setPrimogems(player.getPrimogems() + count);
|
||||||
case 202 -> // Mora
|
case 202 -> // Mora
|
||||||
@ -275,6 +278,8 @@ public class Inventory implements Iterable<GameItem> {
|
|||||||
return player.getMora();
|
return player.getMora();
|
||||||
case 203: // Genesis Crystals
|
case 203: // Genesis Crystals
|
||||||
return player.getCrystals();
|
return player.getCrystals();
|
||||||
|
case 106: // Resin
|
||||||
|
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||||
default:
|
default:
|
||||||
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
|
GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S
|
||||||
return (item == null) ? 0 : item.getCount();
|
return (item == null) ? 0 : item.getCount();
|
||||||
@ -313,6 +318,8 @@ public class Inventory implements Iterable<GameItem> {
|
|||||||
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
||||||
case 203 -> // Genesis Crystals
|
case 203 -> // Genesis Crystals
|
||||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||||
|
case 106 -> // Resin
|
||||||
|
player.getResinManager().useResin(cost.getCount() * quantity);
|
||||||
default ->
|
default ->
|
||||||
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import emu.grasscutter.data.excels.ItemData;
|
|||||||
import emu.grasscutter.game.inventory.GameItem;
|
import emu.grasscutter.game.inventory.GameItem;
|
||||||
import emu.grasscutter.game.inventory.ItemType;
|
import emu.grasscutter.game.inventory.ItemType;
|
||||||
import emu.grasscutter.game.player.Player;
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.ActionReason;
|
||||||
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
|
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
|
||||||
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
|
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
|
||||||
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
|
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
|
||||||
@ -147,38 +148,16 @@ public class ForgingManager {
|
|||||||
|
|
||||||
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
|
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
|
||||||
|
|
||||||
// Check if we have enough of each material.
|
// Check if we have enough of each material and consume.
|
||||||
for (var material : forgeData.getMaterialItems()) {
|
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
|
||||||
if (material.getItemId() == 0) {
|
material.add(new ItemParamData(202, forgeData.getScoinCost()));
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentCount = this.player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(material.getItemId()).getCount();
|
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
|
||||||
|
|
||||||
if (currentCount < material.getCount() * req.getForgeCount()) {
|
if (!success) {
|
||||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have enough Mora.
|
|
||||||
if (this.player.getMora() < forgeData.getScoinCost() * req.getForgeCount()) {
|
|
||||||
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
|
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume material and Mora.
|
|
||||||
for (var material : forgeData.getMaterialItems()) {
|
|
||||||
if (material.getItemId() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
GameItem item = this.player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(material.getItemId());
|
|
||||||
this.player.getInventory().removeItem(item, material.getCount() * req.getForgeCount());
|
|
||||||
}
|
|
||||||
|
|
||||||
this.player.setMora(this.player.getMora() - forgeData.getScoinCost() * req.getForgeCount());
|
|
||||||
|
|
||||||
// Create and add active forge.
|
// Create and add active forge.
|
||||||
ActiveForgeData activeForge = new ActiveForgeData();
|
ActiveForgeData activeForge = new ActiveForgeData();
|
||||||
activeForge.setForgeId(req.getForgeId());
|
activeForge.setForgeId(req.getForgeId());
|
||||||
|
@ -856,6 +856,27 @@ public class InventoryManager {
|
|||||||
// Unlock.
|
// Unlock.
|
||||||
useSuccess = player.getForgingManager().unlockForgingBlueprint(useItem);
|
useSuccess = player.getForgingManager().unlockForgingBlueprint(useItem);
|
||||||
}
|
}
|
||||||
|
// Handle combine diagrams.
|
||||||
|
if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) {
|
||||||
|
// Unlock.
|
||||||
|
useSuccess = player.getServer().getCombineManger().unlockCombineDiagram(player, useItem);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MATERIAL_CONSUME_BATCH_USE:
|
||||||
|
// Make sure we have usage data for this material.
|
||||||
|
if (useItem.getItemData().getItemUse() == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle fragile/transient resin.
|
||||||
|
if (useItem.getItemId() == 107009 || useItem.getItemId() == 107012){
|
||||||
|
// Add resin to the inventory.
|
||||||
|
ItemData resinItemData = GameData.getItemDataMap().get(106);
|
||||||
|
player.getInventory().addItem(new GameItem(resinItemData, 60 * count), ActionReason.PlayerUseItem);
|
||||||
|
|
||||||
|
// Set used amount.
|
||||||
|
used = count;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case MATERIAL_CHEST:
|
case MATERIAL_CHEST:
|
||||||
List<ShopChestTable> shopChestTableList = player.getServer().getShopManager().getShopChestData();
|
List<ShopChestTable> shopChestTableList = player.getServer().getShopManager().getShopChestData();
|
||||||
|
143
src/main/java/emu/grasscutter/game/managers/ResinManager.java
Normal file
143
src/main/java/emu/grasscutter/game/managers/ResinManager.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package emu.grasscutter.game.managers;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketResinChangeNotify;
|
||||||
|
import emu.grasscutter.utils.Utils;
|
||||||
|
|
||||||
|
import static emu.grasscutter.Configuration.GAME_OPTIONS;
|
||||||
|
|
||||||
|
public class ResinManager {
|
||||||
|
private final Player player;
|
||||||
|
|
||||||
|
public ResinManager(Player player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Change resin.
|
||||||
|
********************/
|
||||||
|
public synchronized boolean useResin(int amount) {
|
||||||
|
// Check if resin enabled.
|
||||||
|
if (!GAME_OPTIONS.resinOptions.resinUsage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||||
|
|
||||||
|
// Check if the player has sufficient resin.
|
||||||
|
if (currentResin < amount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduct the resin from the player.
|
||||||
|
int newResin = currentResin - amount;
|
||||||
|
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, newResin);
|
||||||
|
|
||||||
|
// Check if this has taken the player under the recharge cap,
|
||||||
|
// starting the recharging process.
|
||||||
|
if (this.player.getNextResinRefresh() == 0 && newResin < GAME_OPTIONS.resinOptions.cap) {
|
||||||
|
int currentTime = Utils.getCurrentSeconds();
|
||||||
|
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send packets.
|
||||||
|
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
|
||||||
|
this.player.sendPacket(new PacketResinChangeNotify(this.player));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void addResin(int amount) {
|
||||||
|
// Check if resin enabled.
|
||||||
|
if (!GAME_OPTIONS.resinOptions.resinUsage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add resin.
|
||||||
|
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||||
|
int newResin = currentResin + amount;
|
||||||
|
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, newResin);
|
||||||
|
|
||||||
|
// Stop recharging if player is now at or over the cap.
|
||||||
|
if (newResin >= GAME_OPTIONS.resinOptions.cap) {
|
||||||
|
this.player.setNextResinRefresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send packets.
|
||||||
|
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
|
||||||
|
this.player.sendPacket(new PacketResinChangeNotify(this.player));
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Recharge resin.
|
||||||
|
********************/
|
||||||
|
public synchronized void rechargeResin() {
|
||||||
|
// Check if resin enabled.
|
||||||
|
if (!GAME_OPTIONS.resinOptions.resinUsage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||||
|
int currentTime = Utils.getCurrentSeconds();
|
||||||
|
|
||||||
|
// Make sure we are currently in "recharging mode".
|
||||||
|
// This is denoted by Player.nextResinRefresh being greater than 0.
|
||||||
|
if (this.player.getNextResinRefresh() <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we actually need to recharge yet.
|
||||||
|
if (currentTime < this.player.getNextResinRefresh()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate how much resin we need to refill and update player.
|
||||||
|
// Note that this can be more than one in case the player
|
||||||
|
// logged off with uncapped resin and is now logging in again.
|
||||||
|
int recharge = 1 + (int)((currentTime - this.player.getNextResinRefresh()) / GAME_OPTIONS.resinOptions.rechargeTime);
|
||||||
|
int newResin = Math.min(GAME_OPTIONS.resinOptions.cap, currentResin + recharge);
|
||||||
|
int resinChange = newResin - currentResin;
|
||||||
|
|
||||||
|
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, newResin);
|
||||||
|
|
||||||
|
// Calculate next recharge time.
|
||||||
|
// Set to zero to disable recharge (because on/over cap.)
|
||||||
|
if (newResin >= GAME_OPTIONS.resinOptions.cap) {
|
||||||
|
this.player.setNextResinRefresh(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int nextRecharge = this.player.getNextResinRefresh() + resinChange * GAME_OPTIONS.resinOptions.rechargeTime;
|
||||||
|
this.player.setNextResinRefresh(nextRecharge);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send packets.
|
||||||
|
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
|
||||||
|
this.player.sendPacket(new PacketResinChangeNotify(this.player));
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Player login.
|
||||||
|
********************/
|
||||||
|
public synchronized void onPlayerLogin() {
|
||||||
|
// If resin usage is disabled, set resin to cap.
|
||||||
|
if (!GAME_OPTIONS.resinOptions.resinUsage) {
|
||||||
|
this.player.setProperty(PlayerProperty.PROP_PLAYER_RESIN, GAME_OPTIONS.resinOptions.cap);
|
||||||
|
this.player.setNextResinRefresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case server administrators change the resin cap while players are capped,
|
||||||
|
// we need to restart recharging here.
|
||||||
|
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||||
|
int currentTime = Utils.getCurrentSeconds();
|
||||||
|
|
||||||
|
if (currentResin < GAME_OPTIONS.resinOptions.cap && this.player.getNextResinRefresh() == 0) {
|
||||||
|
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send initial notifications on logon.
|
||||||
|
this.player.sendPacket(new PacketPlayerPropNotify(this.player, PlayerProperty.PROP_PLAYER_RESIN));
|
||||||
|
this.player.sendPacket(new PacketResinChangeNotify(this.player));
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import emu.grasscutter.game.inventory.Inventory;
|
|||||||
import emu.grasscutter.game.mail.Mail;
|
import emu.grasscutter.game.mail.Mail;
|
||||||
import emu.grasscutter.game.mail.MailHandler;
|
import emu.grasscutter.game.mail.MailHandler;
|
||||||
import emu.grasscutter.game.managers.InsectCaptureManager;
|
import emu.grasscutter.game.managers.InsectCaptureManager;
|
||||||
|
import emu.grasscutter.game.managers.ResinManager;
|
||||||
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
||||||
import emu.grasscutter.game.managers.SotSManager;
|
import emu.grasscutter.game.managers.SotSManager;
|
||||||
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
|
import emu.grasscutter.game.managers.EnergyManager.EnergyManager;
|
||||||
@ -48,6 +49,7 @@ import emu.grasscutter.net.proto.*;
|
|||||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||||
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
|
||||||
|
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||||
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
import emu.grasscutter.net.proto.MpSettingTypeOuterClass.MpSettingType;
|
||||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||||
@ -94,6 +96,7 @@ public class Player {
|
|||||||
private Set<Integer> flyCloakList;
|
private Set<Integer> flyCloakList;
|
||||||
private Set<Integer> costumeList;
|
private Set<Integer> costumeList;
|
||||||
private Set<Integer> unlockedForgingBlueprints;
|
private Set<Integer> unlockedForgingBlueprints;
|
||||||
|
private Set<Integer> unlockedCombines;
|
||||||
private List<ActiveForgeData> activeForges;
|
private List<ActiveForgeData> activeForges;
|
||||||
|
|
||||||
private Integer widgetId;
|
private Integer widgetId;
|
||||||
@ -158,11 +161,13 @@ public class Player {
|
|||||||
@Transient private MapMarksManager mapMarksManager;
|
@Transient private MapMarksManager mapMarksManager;
|
||||||
@Transient private StaminaManager staminaManager;
|
@Transient private StaminaManager staminaManager;
|
||||||
@Transient private EnergyManager energyManager;
|
@Transient private EnergyManager energyManager;
|
||||||
|
@Transient private ResinManager resinManager;
|
||||||
@Transient private ForgingManager forgingManager;
|
@Transient private ForgingManager forgingManager;
|
||||||
@Transient private DeforestationManager deforestationManager;
|
@Transient private DeforestationManager deforestationManager;
|
||||||
|
|
||||||
private long springLastUsed;
|
private long springLastUsed;
|
||||||
private HashMap<String, MapMark> mapMarks;
|
private HashMap<String, MapMark> mapMarks;
|
||||||
|
private int nextResinRefresh;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||||
@ -193,6 +198,7 @@ public class Player {
|
|||||||
this.costumeList = new HashSet<>();
|
this.costumeList = new HashSet<>();
|
||||||
this.towerData = new TowerData();
|
this.towerData = new TowerData();
|
||||||
this.unlockedForgingBlueprints = new HashSet<>();
|
this.unlockedForgingBlueprints = new HashSet<>();
|
||||||
|
this.unlockedCombines = new HashSet<>();
|
||||||
this.activeForges = new ArrayList<>();
|
this.activeForges = new ArrayList<>();
|
||||||
|
|
||||||
this.setSceneId(3);
|
this.setSceneId(3);
|
||||||
@ -217,6 +223,7 @@ public class Player {
|
|||||||
this.staminaManager = new StaminaManager(this);
|
this.staminaManager = new StaminaManager(this);
|
||||||
this.sotsManager = new SotSManager(this);
|
this.sotsManager = new SotSManager(this);
|
||||||
this.energyManager = new EnergyManager(this);
|
this.energyManager = new EnergyManager(this);
|
||||||
|
this.resinManager = new ResinManager(this);
|
||||||
this.forgingManager = new ForgingManager(this);
|
this.forgingManager = new ForgingManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,6 +255,7 @@ public class Player {
|
|||||||
this.staminaManager = new StaminaManager(this);
|
this.staminaManager = new StaminaManager(this);
|
||||||
this.sotsManager = new SotSManager(this);
|
this.sotsManager = new SotSManager(this);
|
||||||
this.energyManager = new EnergyManager(this);
|
this.energyManager = new EnergyManager(this);
|
||||||
|
this.resinManager = new ResinManager(this);
|
||||||
this.deforestationManager = new DeforestationManager(this);
|
this.deforestationManager = new DeforestationManager(this);
|
||||||
this.forgingManager = new ForgingManager(this);
|
this.forgingManager = new ForgingManager(this);
|
||||||
}
|
}
|
||||||
@ -540,6 +548,10 @@ public class Player {
|
|||||||
return this.unlockedForgingBlueprints;
|
return this.unlockedForgingBlueprints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<Integer> getUnlockedCombines() {
|
||||||
|
return this.unlockedCombines;
|
||||||
|
}
|
||||||
|
|
||||||
public List<ActiveForgeData> getActiveForges() {
|
public List<ActiveForgeData> getActiveForges() {
|
||||||
return this.activeForges;
|
return this.activeForges;
|
||||||
}
|
}
|
||||||
@ -648,6 +660,14 @@ public class Player {
|
|||||||
springLastUsed = val;
|
springLastUsed = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNextResinRefresh() {
|
||||||
|
return nextResinRefresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextResinRefresh(int value) {
|
||||||
|
this.nextResinRefresh = value;
|
||||||
|
}
|
||||||
|
|
||||||
public SceneLoadState getSceneLoadState() {
|
public SceneLoadState getSceneLoadState() {
|
||||||
return sceneState;
|
return sceneState;
|
||||||
}
|
}
|
||||||
@ -940,7 +960,7 @@ public class Player {
|
|||||||
return this.getMailHandler().replaceMailByIndex(index, message);
|
return this.getMailHandler().replaceMailByIndex(index, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void interactWith(int gadgetEntityId) {
|
public void interactWith(int gadgetEntityId, GadgetInteractReq request) {
|
||||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
return;
|
return;
|
||||||
@ -970,7 +990,7 @@ public class Player {
|
|||||||
} else if (entity instanceof EntityGadget gadget) {
|
} else if (entity instanceof EntityGadget gadget) {
|
||||||
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) {
|
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) {
|
||||||
if (scene.getChallenge() != null) {
|
if (scene.getChallenge() != null) {
|
||||||
scene.getChallenge().getStatueDrops(this);
|
scene.getChallenge().getStatueDrops(this, request);
|
||||||
}
|
}
|
||||||
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE));
|
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE));
|
||||||
}
|
}
|
||||||
@ -1142,6 +1162,10 @@ public class Player {
|
|||||||
return this.energyManager;
|
return this.energyManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResinManager getResinManager() {
|
||||||
|
return this.resinManager;
|
||||||
|
}
|
||||||
|
|
||||||
public ForgingManager getForgingManager() {
|
public ForgingManager getForgingManager() {
|
||||||
return this.forgingManager;
|
return this.forgingManager;
|
||||||
}
|
}
|
||||||
@ -1208,6 +1232,9 @@ public class Player {
|
|||||||
|
|
||||||
// Send updated forge queue data, if necessary.
|
// Send updated forge queue data, if necessary.
|
||||||
this.getForgingManager().sendPlayerForgingUpdate();
|
this.getForgingManager().sendPlayerForgingUpdate();
|
||||||
|
|
||||||
|
// Recharge resin.
|
||||||
|
this.getResinManager().rechargeResin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1291,7 +1318,9 @@ public class Player {
|
|||||||
session.send(new PacketWidgetGadgetAllDataNotify());
|
session.send(new PacketWidgetGadgetAllDataNotify());
|
||||||
session.send(new PacketPlayerHomeCompInfoNotify(this));
|
session.send(new PacketPlayerHomeCompInfoNotify(this));
|
||||||
session.send(new PacketHomeComfortInfoNotify(this));
|
session.send(new PacketHomeComfortInfoNotify(this));
|
||||||
|
session.send(new PacketCombineDataNotify(this.unlockedCombines));
|
||||||
this.forgingManager.sendForgeDataNotify();
|
this.forgingManager.sendForgeDataNotify();
|
||||||
|
this.resinManager.onPlayerLogin();
|
||||||
|
|
||||||
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
|
getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ public class HandlerGadgetInteractReq extends PacketHandler {
|
|||||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||||
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
GadgetInteractReq req = GadgetInteractReq.parseFrom(payload);
|
||||||
|
|
||||||
session.getPlayer().interactWith(req.getGadgetEntityId());
|
session.getPlayer().interactWith(req.getGadgetEntityId(), req);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.CombineDataNotifyOuterClass.CombineDataNotify;
|
||||||
|
|
||||||
|
public class PacketCombineDataNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketCombineDataNotify(Iterable<Integer> unlockedCombines) {
|
||||||
|
super(PacketOpcodes.CombineDataNotify);
|
||||||
|
|
||||||
|
CombineDataNotify proto = CombineDataNotify.newBuilder()
|
||||||
|
.addAllCombineIdList(unlockedCombines)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.CombineFormulaDataNotifyOuterClass.CombineFormulaDataNotify;
|
||||||
|
|
||||||
|
public class PacketCombineFormulaDataNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketCombineFormulaDataNotify(int combineId) {
|
||||||
|
super(PacketOpcodes.CombineFormulaDataNotify);
|
||||||
|
|
||||||
|
CombineFormulaDataNotify proto = CombineFormulaDataNotify.newBuilder()
|
||||||
|
.setCombineId(combineId)
|
||||||
|
.setIsLocked(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package emu.grasscutter.server.packet.send;
|
||||||
|
|
||||||
|
import emu.grasscutter.game.player.Player;
|
||||||
|
import emu.grasscutter.game.props.PlayerProperty;
|
||||||
|
import emu.grasscutter.net.packet.BasePacket;
|
||||||
|
import emu.grasscutter.net.packet.PacketOpcodes;
|
||||||
|
import emu.grasscutter.net.proto.ResinChangeNotifyOuterClass.ResinChangeNotify;
|
||||||
|
|
||||||
|
public class PacketResinChangeNotify extends BasePacket {
|
||||||
|
|
||||||
|
public PacketResinChangeNotify(Player player) {
|
||||||
|
super(PacketOpcodes.ResinChangeNotify);
|
||||||
|
|
||||||
|
ResinChangeNotify proto = ResinChangeNotify.newBuilder()
|
||||||
|
.setCurValue(player.getProperty(PlayerProperty.PROP_PLAYER_RESIN))
|
||||||
|
.setNextAddTimestamp(player.getNextResinRefresh())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// ToDo: Add ability to buy resin with primogems, has to be included here.
|
||||||
|
|
||||||
|
this.setData(proto);
|
||||||
|
}
|
||||||
|
}
|
@ -177,6 +177,7 @@ public class ConfigContainer {
|
|||||||
public boolean enableShopItems = true;
|
public boolean enableShopItems = true;
|
||||||
public boolean staminaUsage = true;
|
public boolean staminaUsage = true;
|
||||||
public boolean energyUsage = false;
|
public boolean energyUsage = false;
|
||||||
|
public ResinOptions resinOptions = new ResinOptions();
|
||||||
public Rates rates = new Rates();
|
public Rates rates = new Rates();
|
||||||
|
|
||||||
public static class InventoryLimits {
|
public static class InventoryLimits {
|
||||||
@ -197,6 +198,12 @@ public class ConfigContainer {
|
|||||||
public float mora = 1.0f;
|
public float mora = 1.0f;
|
||||||
public float leyLines = 1.0f;
|
public float leyLines = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ResinOptions {
|
||||||
|
public boolean resinUsage = false;
|
||||||
|
public int cap = 160;
|
||||||
|
public int rechargeTime = 480;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class JoinOptions {
|
public static class JoinOptions {
|
||||||
|
Loading…
Reference in New Issue
Block a user