mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-06-26 10:04:58 +08:00
Compare commits
4 Commits
2560b14557
...
f10dc1cf8d
Author | SHA1 | Date | |
---|---|---|---|
|
f10dc1cf8d | ||
|
64d74387a2 | ||
|
f2f6993b3d | ||
|
ded7ce1dcf |
@ -37,7 +37,7 @@ EN | [中文](README_zh-CN.md)
|
||||
**Note:** If you updated from an older version, delete `config.json` to regenerate it.
|
||||
|
||||
1. Get `grasscutter.jar`
|
||||
- Download from [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip)
|
||||
- Download from [actions](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297)
|
||||
- [Build by yourself](#Building)
|
||||
2. Create a `resources` folder in the directory where grasscutter.jar is located and move your `BinOutput` and `ExcelBinOutput` folders there *(Check the [wiki](https://github.com/Grasscutters/Grasscutter/wiki) for more details how to get those.)*
|
||||
3. Run Grasscutter with `java -jar grasscutter.jar`. **Make sure mongodb service is running as well.**
|
||||
|
@ -37,7 +37,7 @@
|
||||
**注:** 如果您从旧版本升级到新版本,最好删除 `config.json` 并启动服务端jar来重新生成它
|
||||
|
||||
1. 获取 `grasscutter.jar`
|
||||
- 从 [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip) 中下载
|
||||
- 从 [actions](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297) 中下载
|
||||
- [自行构建](#构建)
|
||||
2. 在**grasscutter.jar** 所在目录中创建 `resources` 文件夹并将 `BinOutput` 和 `ExcelBinOutput` 放入其中 *(查看 [wiki](https://github.com/Grasscutters/Grasscutter/wiki) 了解更多)*
|
||||
3. 通过命令 `java -jar grasscutter.jar` 来运行Grasscutter. **在此之前请确认MongoDB服务运行正常**
|
||||
|
@ -8,7 +8,7 @@ import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "kick", usage = "kick", permission = "server.kick", permissionTargeted = "server.kick.others", description = "commands.kick.description")
|
||||
@Command(label = "kick", usage = "kick", permission = "server.kick", description = "commands.kick.description")
|
||||
public final class KickCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
|
@ -1,9 +1,16 @@
|
||||
package emu.grasscutter.data.binout;
|
||||
|
||||
import java.util.Map;
|
||||
import java.io.Serializable;
|
||||
|
||||
public class AbilityModifier {
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class AbilityModifier implements Serializable {
|
||||
private static final long serialVersionUID = -2001232313615923575L;
|
||||
|
||||
@SerializedName(value="onAdded", alternate={"KCICDEJLIJD"})
|
||||
public AbilityModifierAction[] onAdded;
|
||||
@SerializedName(value="onThinkInterval", alternate={"PBDDACFFPOE"})
|
||||
public AbilityModifierAction[] onThinkInterval;
|
||||
public AbilityModifierAction[] onRemoved;
|
||||
|
||||
@ -13,6 +20,7 @@ public class AbilityModifier {
|
||||
|
||||
public static class AbilityData {
|
||||
public String abilityName;
|
||||
@SerializedName(value="modifiers", alternate={"HNEIEGHMLKH"})
|
||||
public Map<String, AbilityModifier> modifiers;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@ public class DungeonData extends GameResource {
|
||||
|
||||
private RewardPreviewData previewData;
|
||||
|
||||
private int statueCostID;
|
||||
private int statueCostCount;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.id;
|
||||
@ -33,6 +36,14 @@ public class DungeonData extends GameResource {
|
||||
return previewData;
|
||||
}
|
||||
|
||||
public int getStatueCostID() {
|
||||
return statueCostID;
|
||||
}
|
||||
|
||||
public int getStatueCostCount() {
|
||||
return statueCostCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.passRewardPreviewID > 0) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package emu.grasscutter.game.ability;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
@ -28,12 +30,15 @@ import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalar
|
||||
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
|
||||
public class AbilityManager {
|
||||
private Player player;
|
||||
HealAbilityManager healAbilityManager;
|
||||
|
||||
public AbilityManager(Player player) {
|
||||
this.player = player;
|
||||
this.healAbilityManager = new HealAbilityManager(player);
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
@ -41,7 +46,9 @@ public class AbilityManager {
|
||||
}
|
||||
|
||||
public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception {
|
||||
// Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
|
||||
healAbilityManager.healHandler(invoke);
|
||||
|
||||
//Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray()));
|
||||
switch (invoke.getArgumentType()) {
|
||||
case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM:
|
||||
handleOverrideParam(invoke);
|
||||
@ -61,6 +68,7 @@ public class AbilityManager {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception {
|
||||
@ -155,19 +163,6 @@ public class AbilityManager {
|
||||
private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) {
|
||||
switch (action.type) {
|
||||
case HealHP -> {
|
||||
if (action.amount == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
float healAmount = 0;
|
||||
|
||||
if (action.amount.isDynamic && action.amount.dynamicKey != null) {
|
||||
healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f);
|
||||
}
|
||||
|
||||
if (healAmount > 0) {
|
||||
target.heal(healAmount);
|
||||
}
|
||||
}
|
||||
case LoseHP -> {
|
||||
if (action.amountByTargetCurrentHPRatio == null) {
|
||||
|
@ -0,0 +1,185 @@
|
||||
package emu.grasscutter.game.ability;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Optional;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.binout.AbilityModifierEntry;
|
||||
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
|
||||
import emu.grasscutter.data.excels.AvatarSkillDepotData;
|
||||
import emu.grasscutter.data.excels.ItemData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.entity.EntityClientGadget;
|
||||
import emu.grasscutter.game.entity.EntityItem;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ElementType;
|
||||
import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead;
|
||||
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
|
||||
import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange;
|
||||
import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap;
|
||||
import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina;
|
||||
import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry;
|
||||
import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
|
||||
public class HealAbilityManager {
|
||||
private class HealData {
|
||||
public boolean isString = true;
|
||||
public String abilityType = ""; //"E" or "Q"
|
||||
public String sRatio = "";
|
||||
public String sBase = "";
|
||||
public float fRatio = 0;
|
||||
public float fBase = 0;
|
||||
public boolean healAll = false;
|
||||
|
||||
public HealData(String _abilityType, String _sRatio, String _sBase, boolean _healAll) {
|
||||
abilityType = _abilityType;
|
||||
isString = true;
|
||||
sRatio = _sRatio;
|
||||
sBase = _sBase;
|
||||
healAll = _healAll;
|
||||
}
|
||||
|
||||
public HealData(String _abilityType, String _sRatio, float _fRatio, float _fBase, boolean _healAll) {
|
||||
abilityType = _abilityType;
|
||||
isString = false;
|
||||
sRatio = _sRatio;
|
||||
fRatio = _fRatio;
|
||||
fBase = _fBase;
|
||||
healAll = _healAll;
|
||||
}
|
||||
}
|
||||
|
||||
private class HealDataAvatar {
|
||||
public static int MAX_NUM_HEAL_ABILITY = 4;
|
||||
public String avatar = "";
|
||||
public int fightPropertyType= 0; //0: maxHP, 1: curAttack, 2: curDefense
|
||||
public ArrayList<HealData> healDataList;
|
||||
|
||||
public HealDataAvatar(String _avatar, int _fightPropertyType) {
|
||||
avatar = _avatar;
|
||||
fightPropertyType = _fightPropertyType;
|
||||
healDataList = new ArrayList();
|
||||
}
|
||||
|
||||
public HealDataAvatar addHealData(String abilityType, String sRatio, String sBase, boolean healAll) {
|
||||
HealData healData = new HealData(abilityType, sRatio, sBase, healAll);
|
||||
healDataList.add(healData);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HealDataAvatar addHealData(String abilityType, String sRatio, float fRatio, float fBase, boolean healAll) {
|
||||
HealData healData = new HealData(abilityType, sRatio, fRatio, fBase, healAll);
|
||||
healDataList.add(healData);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<HealDataAvatar> healDataAvatarList;
|
||||
private Player player;
|
||||
|
||||
public HealAbilityManager (Player player) {
|
||||
this.player = player;
|
||||
healDataAvatarList = new ArrayList();
|
||||
healDataAvatarList.add(new HealDataAvatar("Kokomi", 0).addHealData("E", "ElementalArt_Heal_MaxHP_Base_Percentage", "ElementalArt_Heal_Base_Amount", false));
|
||||
healDataAvatarList.add(new HealDataAvatar("Qin", 1).addHealData("Q", "Heal", "BurstHealConst", true));
|
||||
healDataAvatarList.add(new HealDataAvatar("Noel", 2).addHealData("E", "OnAttack_HealthRate", 0.452f, 282f, true));
|
||||
healDataAvatarList.add(new HealDataAvatar("Bennett", 0).addHealData("Q", "HealMaxHpRatio", "HealConst", false));
|
||||
healDataAvatarList.add(new HealDataAvatar("Diona", 0).addHealData("Q", "HealHPRatio", "HealHP_Const", false));
|
||||
healDataAvatarList.add(new HealDataAvatar("Sayu", 1).addHealData("Q", "Constellation_6_Damage", "Heal_BaseAmount", true).addHealData("Q", "Heal_AttackRatio", "Constellation_6_Heal", true));
|
||||
healDataAvatarList.add(new HealDataAvatar("Barbara", 0).addHealData("E", "HealHPOnAdded", "HealHPOnAdded_Const", true).addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true));
|
||||
healDataAvatarList.add(new HealDataAvatar("Shinobu", 0).addHealData("E", "ElementalArt_Heal_MaxHP_Percentage", 0.064f, 795f, false));
|
||||
healDataAvatarList.add(new HealDataAvatar("Qiqi", 1).addHealData("E", "HealHP_OnHittingOthers", "HealHP_Const_OnHittingOthers", true).addHealData("E", "ElementalArt_HealHp_Ratio", "ElementalArt_HealHp_Const", true));
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public void healHandler(AbilityInvokeEntry invoke) throws Exception {
|
||||
AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData());
|
||||
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId());
|
||||
|
||||
String modifierString = "";
|
||||
if(data.getParentAbilityName() != null)
|
||||
modifierString = data.getParentAbilityName().getStr();
|
||||
|
||||
if(sourceEntity != null)
|
||||
checkAndHeal(sourceEntity, modifierString);
|
||||
}
|
||||
|
||||
public void checkAndHeal(GameEntity sourceEntity, String modifierString) {
|
||||
int fightPropertyType = 0;
|
||||
float healAmount = 0;
|
||||
float ratio = 0, base = 0;
|
||||
float maxHP, curAttack, curDefense;
|
||||
Map<String, Float> map = sourceEntity.getMetaOverrideMap();
|
||||
for(int i = 0 ; i < healDataAvatarList.size() ; i ++) {
|
||||
HealDataAvatar healDataAvatar = healDataAvatarList.get(i);
|
||||
if(modifierString.contains(healDataAvatar.avatar)) {
|
||||
fightPropertyType = healDataAvatar.fightPropertyType;
|
||||
ArrayList<HealData> healDataList = healDataAvatar.healDataList;
|
||||
|
||||
for(int j = 0 ; j < healDataList.size(); j++) {
|
||||
HealData healData = healDataList.get(j);
|
||||
if(map.containsKey(healData.sRatio)) {
|
||||
if(healData.isString) {
|
||||
ratio = map.get(healData.sRatio);
|
||||
base = map.get(healData.sBase);
|
||||
}
|
||||
else {
|
||||
ratio = healData.fRatio;
|
||||
base = healData.fBase;
|
||||
}
|
||||
}
|
||||
|
||||
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
|
||||
List<EntityAvatar> needHealAvatars = new ArrayList();
|
||||
int currentIndex = player.getTeamManager().getCurrentCharacterIndex();
|
||||
EntityAvatar currentAvatar = activeTeam.get(currentIndex);
|
||||
if(healData.healAll) {
|
||||
needHealAvatars = activeTeam;
|
||||
}
|
||||
else {
|
||||
needHealAvatars.add(currentAvatar);
|
||||
}
|
||||
|
||||
maxHP = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
|
||||
curAttack = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK);
|
||||
curDefense = currentAvatar.getFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE);
|
||||
switch(fightPropertyType) {
|
||||
case 0:
|
||||
healAmount = ratio * maxHP + base;
|
||||
break;
|
||||
case 1:
|
||||
healAmount = ratio * curAttack + base;
|
||||
break;
|
||||
case 2:
|
||||
healAmount = ratio * curDefense + base;
|
||||
break;
|
||||
}
|
||||
for(int k = 0 ; k < needHealAvatars.size() ; k ++) {
|
||||
EntityAvatar avatar = needHealAvatars.get(k);
|
||||
avatar.heal(healAmount);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,13 +3,17 @@ package emu.grasscutter.game.combine;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.CombineData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.RetcodeOuterClass;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketCombineRsp;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CombineManger {
|
||||
@ -23,6 +27,27 @@ public class CombineManger {
|
||||
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){
|
||||
// check config exist
|
||||
if(!GameData.getCombineDataMap().containsKey(cid)){
|
||||
@ -35,36 +60,17 @@ public class CombineManger {
|
||||
if(combineData.getPlayerLevel() > player.getLevel()){
|
||||
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
|
||||
if(!enough){
|
||||
player.getWorld().getHost().sendPacket(
|
||||
new PacketCombineRsp(RetcodeOuterClass.Retcode.RET_ITEM_COMBINE_COUNT_NOT_ENOUGH_VALUE)
|
||||
);
|
||||
return null;
|
||||
// consume items
|
||||
List<ItemParamData> material = new ArrayList<>(combineData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, combineData.getScoinCost()));
|
||||
|
||||
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), count, ActionReason.Combine);
|
||||
|
||||
// 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
|
||||
player.getInventory().addItem(combineData.getResultItemId(),
|
||||
|
@ -4,9 +4,11 @@ import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.DungeonData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
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();
|
||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||
|
||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
@ -164,11 +178,43 @@ public class DungeonChallenge {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rewards.
|
||||
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.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
|
@ -17,6 +17,7 @@ import emu.grasscutter.game.avatar.AvatarStorage;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarEquipChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
||||
@ -258,6 +259,8 @@ public class Inventory implements Iterable<GameItem> {
|
||||
getPlayer().addExpDirectly(count);
|
||||
case 105 -> // Companionship exp
|
||||
getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count);
|
||||
case 106 -> // Resin
|
||||
getPlayer().getResinManager().addResin(count);
|
||||
case 201 -> // Primogem
|
||||
getPlayer().setPrimogems(player.getPrimogems() + count);
|
||||
case 202 -> // Mora
|
||||
@ -275,6 +278,8 @@ public class Inventory implements Iterable<GameItem> {
|
||||
return player.getMora();
|
||||
case 203: // Genesis Crystals
|
||||
return player.getCrystals();
|
||||
case 106: // Resin
|
||||
return player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
|
||||
default:
|
||||
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();
|
||||
@ -313,6 +318,8 @@ public class Inventory implements Iterable<GameItem> {
|
||||
player.setMora(player.getMora() - (cost.getCount() * quantity));
|
||||
case 203 -> // Genesis Crystals
|
||||
player.setCrystals(player.getCrystals() - (cost.getCount() * quantity));
|
||||
case 106 -> // Resin
|
||||
player.getResinManager().useResin(cost.getCount() * quantity);
|
||||
default ->
|
||||
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.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.ForgeStartReqOuterClass;
|
||||
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
|
||||
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
|
||||
@ -147,37 +148,15 @@ public class ForgingManager {
|
||||
|
||||
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
|
||||
|
||||
// Check if we have enough of each material.
|
||||
for (var material : forgeData.getMaterialItems()) {
|
||||
if (material.getItemId() == 0) {
|
||||
continue;
|
||||
}
|
||||
// Check if we have enough of each material and consume.
|
||||
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
|
||||
material.add(new ItemParamData(202, forgeData.getScoinCost()));
|
||||
|
||||
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.
|
||||
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.
|
||||
ActiveForgeData activeForge = new ActiveForgeData();
|
||||
|
@ -856,6 +856,27 @@ public class InventoryManager {
|
||||
// Unlock.
|
||||
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;
|
||||
case MATERIAL_CHEST:
|
||||
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.MailHandler;
|
||||
import emu.grasscutter.game.managers.InsectCaptureManager;
|
||||
import emu.grasscutter.game.managers.ResinManager;
|
||||
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
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.AttackResultOuterClass.AttackResult;
|
||||
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.MpSettingTypeOuterClass.MpSettingType;
|
||||
import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo;
|
||||
@ -94,6 +96,7 @@ public class Player {
|
||||
private Set<Integer> flyCloakList;
|
||||
private Set<Integer> costumeList;
|
||||
private Set<Integer> unlockedForgingBlueprints;
|
||||
private Set<Integer> unlockedCombines;
|
||||
private List<ActiveForgeData> activeForges;
|
||||
|
||||
private Integer widgetId;
|
||||
@ -158,11 +161,13 @@ public class Player {
|
||||
@Transient private MapMarksManager mapMarksManager;
|
||||
@Transient private StaminaManager staminaManager;
|
||||
@Transient private EnergyManager energyManager;
|
||||
@Transient private ResinManager resinManager;
|
||||
@Transient private ForgingManager forgingManager;
|
||||
@Transient private DeforestationManager deforestationManager;
|
||||
|
||||
private long springLastUsed;
|
||||
private HashMap<String, MapMark> mapMarks;
|
||||
private int nextResinRefresh;
|
||||
|
||||
@Deprecated
|
||||
@SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
|
||||
@ -193,6 +198,7 @@ public class Player {
|
||||
this.costumeList = new HashSet<>();
|
||||
this.towerData = new TowerData();
|
||||
this.unlockedForgingBlueprints = new HashSet<>();
|
||||
this.unlockedCombines = new HashSet<>();
|
||||
this.activeForges = new ArrayList<>();
|
||||
|
||||
this.setSceneId(3);
|
||||
@ -217,6 +223,7 @@ public class Player {
|
||||
this.staminaManager = new StaminaManager(this);
|
||||
this.sotsManager = new SotSManager(this);
|
||||
this.energyManager = new EnergyManager(this);
|
||||
this.resinManager = new ResinManager(this);
|
||||
this.forgingManager = new ForgingManager(this);
|
||||
}
|
||||
|
||||
@ -248,6 +255,7 @@ public class Player {
|
||||
this.staminaManager = new StaminaManager(this);
|
||||
this.sotsManager = new SotSManager(this);
|
||||
this.energyManager = new EnergyManager(this);
|
||||
this.resinManager = new ResinManager(this);
|
||||
this.deforestationManager = new DeforestationManager(this);
|
||||
this.forgingManager = new ForgingManager(this);
|
||||
}
|
||||
@ -540,6 +548,10 @@ public class Player {
|
||||
return this.unlockedForgingBlueprints;
|
||||
}
|
||||
|
||||
public Set<Integer> getUnlockedCombines() {
|
||||
return this.unlockedCombines;
|
||||
}
|
||||
|
||||
public List<ActiveForgeData> getActiveForges() {
|
||||
return this.activeForges;
|
||||
}
|
||||
@ -648,6 +660,14 @@ public class Player {
|
||||
springLastUsed = val;
|
||||
}
|
||||
|
||||
public int getNextResinRefresh() {
|
||||
return nextResinRefresh;
|
||||
}
|
||||
|
||||
public void setNextResinRefresh(int value) {
|
||||
this.nextResinRefresh = value;
|
||||
}
|
||||
|
||||
public SceneLoadState getSceneLoadState() {
|
||||
return sceneState;
|
||||
}
|
||||
@ -940,7 +960,7 @@ public class Player {
|
||||
return this.getMailHandler().replaceMailByIndex(index, message);
|
||||
}
|
||||
|
||||
public void interactWith(int gadgetEntityId) {
|
||||
public void interactWith(int gadgetEntityId, GadgetInteractReq request) {
|
||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||
if (entity == null) {
|
||||
return;
|
||||
@ -970,7 +990,7 @@ public class Player {
|
||||
} else if (entity instanceof EntityGadget gadget) {
|
||||
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) {
|
||||
if (scene.getChallenge() != null) {
|
||||
scene.getChallenge().getStatueDrops(this);
|
||||
scene.getChallenge().getStatueDrops(this, request);
|
||||
}
|
||||
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE));
|
||||
}
|
||||
@ -1142,6 +1162,10 @@ public class Player {
|
||||
return this.energyManager;
|
||||
}
|
||||
|
||||
public ResinManager getResinManager() {
|
||||
return this.resinManager;
|
||||
}
|
||||
|
||||
public ForgingManager getForgingManager() {
|
||||
return this.forgingManager;
|
||||
}
|
||||
@ -1208,6 +1232,9 @@ public class Player {
|
||||
|
||||
// Send updated forge queue data, if necessary.
|
||||
this.getForgingManager().sendPlayerForgingUpdate();
|
||||
|
||||
// Recharge resin.
|
||||
this.getResinManager().rechargeResin();
|
||||
}
|
||||
|
||||
|
||||
@ -1291,7 +1318,9 @@ public class Player {
|
||||
session.send(new PacketWidgetGadgetAllDataNotify());
|
||||
session.send(new PacketPlayerHomeCompInfoNotify(this));
|
||||
session.send(new PacketHomeComfortInfoNotify(this));
|
||||
session.send(new PacketCombineDataNotify(this.unlockedCombines));
|
||||
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.
|
||||
|
||||
|
@ -13,7 +13,7 @@ public class HandlerGadgetInteractReq extends PacketHandler {
|
||||
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
|
||||
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 staminaUsage = true;
|
||||
public boolean energyUsage = false;
|
||||
public ResinOptions resinOptions = new ResinOptions();
|
||||
public Rates rates = new Rates();
|
||||
|
||||
public static class InventoryLimits {
|
||||
@ -197,6 +198,12 @@ public class ConfigContainer {
|
||||
public float mora = 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 {
|
||||
|
@ -140,6 +140,11 @@
|
||||
"success": "Summoned %s to %s's world.",
|
||||
"description": "Forces someone to join the world of others. If no one is targeted, it sends you into co-op mode anyway."
|
||||
},
|
||||
"drop": {
|
||||
"command_usage": "Usage: drop <itemID|itemName> [amount]",
|
||||
"success": "Dropped %s of %s.",
|
||||
"description": "Drops an item near you"
|
||||
},
|
||||
"enter_dungeon": {
|
||||
"usage": "Usage: enterdungeon <dungeonID>",
|
||||
"changed": "Changed to dungeon %s.",
|
||||
@ -181,19 +186,16 @@
|
||||
"success": "Godmode is now %s for %s.",
|
||||
"description": "Prevents you from taking damage. Defaults to toggle."
|
||||
},
|
||||
"nostamina": {
|
||||
"success": "NoStamina is now %s for %s.",
|
||||
"description": "Keep your endurance to the maximum."
|
||||
},
|
||||
"unlimitenergy": {
|
||||
"success": "UnlimitEnergy is now %s for %s.",
|
||||
"config_error": "Command is disabled, because energyUsage is false in config.json.",
|
||||
"description": "Use the element does not waste energy"
|
||||
},
|
||||
"heal": {
|
||||
"success": "All characters have been healed.",
|
||||
"description": "Heal all characters in your current team."
|
||||
},
|
||||
"help": {
|
||||
"usage": "Usage: ",
|
||||
"aliases": "Aliases: ",
|
||||
"available_commands": "Available commands: ",
|
||||
"description": "Sends the help message or shows information about a specified command"
|
||||
},
|
||||
"kick": {
|
||||
"player_kick_player": "Player [%s:%s] has kicked player [%s:%s]",
|
||||
"server_kick_player": "Kicking player [%s:%s]...",
|
||||
@ -220,6 +222,10 @@
|
||||
"success": "There are %s player(s) online:",
|
||||
"description": "List online players"
|
||||
},
|
||||
"nostamina": {
|
||||
"success": "NoStamina is now %s for %s.",
|
||||
"description": "Keep your endurance to the maximum."
|
||||
},
|
||||
"permission": {
|
||||
"usage": "Usage: permission <add|remove> <username> <permission>",
|
||||
"add": "Permission added.",
|
||||
@ -256,6 +262,9 @@
|
||||
"success": "Reset complete.",
|
||||
"description": "Reset target player's shop refresh time"
|
||||
},
|
||||
"restart": {
|
||||
"description": "Restarts the current session"
|
||||
},
|
||||
"sendMail": {
|
||||
"usage": "Usage: sendmail <userID|all|help> [templateID]",
|
||||
"user_not_exist": "The user with an ID of '%s' does not exist.",
|
||||
@ -368,29 +377,20 @@
|
||||
"success": "Teleported %s to %s, %s, %s in scene %s.",
|
||||
"description": "Change the player's position"
|
||||
},
|
||||
"unlimitenergy": {
|
||||
"success": "UnlimitEnergy is now %s for %s.",
|
||||
"config_error": "Command is disabled, because energyUsage is false in config.json.",
|
||||
"description": "Use the element does not waste energy"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "Abyss Corridor's Floors are all unlocked now.",
|
||||
"description": "Unlock all levels of tower"
|
||||
},
|
||||
"weather": {
|
||||
"usage": "Usage: weather <climate type(weatherId)> <weather type(climateId)>\nWeather types 0: None, 1: Sunny, 2: Cloudy, 3: Rain, 4: Thunderstorm, 5: Snow, 6: Mist",
|
||||
"success": "Changed climate type to %s with weather type %s.",
|
||||
"invalid_id": "Invalid ID.",
|
||||
"description": "Changes the weather"
|
||||
},
|
||||
"drop": {
|
||||
"command_usage": "Usage: drop <itemID|itemName> [amount]",
|
||||
"success": "Dropped %s of %s.",
|
||||
"description": "Drops an item near you"
|
||||
},
|
||||
"help": {
|
||||
"usage": "Usage: ",
|
||||
"aliases": "Aliases: ",
|
||||
"available_commands": "Available commands: ",
|
||||
"description": "Sends the help message or shows information about a specified command"
|
||||
},
|
||||
"restart": {
|
||||
"description": "Restarts the current session"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "Unlock done.",
|
||||
"description": "Unlock Abyss Corridor's Floors"
|
||||
}
|
||||
},
|
||||
"gacha": {
|
||||
|
@ -140,6 +140,11 @@
|
||||
"success": "已强制传送 %s 到 %s 的世界。",
|
||||
"description": "强制传送指定玩家到他人的世界。如果没有指定玩家,则会使你进入多人游戏状态"
|
||||
},
|
||||
"drop": {
|
||||
"command_usage": "用法:drop <物品ID|物品名称> [数量]",
|
||||
"success": "已丢下 %s 个 %s。",
|
||||
"description": "在你附近丢下物品"
|
||||
},
|
||||
"enter_dungeon": {
|
||||
"usage": "用法:enterdungeon <秘境ID>",
|
||||
"changed": "已进入秘境 %s。",
|
||||
@ -181,19 +186,16 @@
|
||||
"success": "Godmode 已设为 %s。[用户:%s]",
|
||||
"description": "防止你受到伤害"
|
||||
},
|
||||
"nostamina": {
|
||||
"success": "NoStamina 已设为 %s。[用户:%s]",
|
||||
"description": "保持你的体力处于最高状态"
|
||||
},
|
||||
"unlimitenergy": {
|
||||
"success": "UnlimitEnergy 已设为 %s。[用户:%s]",
|
||||
"config_error": "命令不可用。因为 config.json 中 energyUsage 为 false。",
|
||||
"description": "使用元素爆发而不消耗能量"
|
||||
},
|
||||
"heal": {
|
||||
"success": "已治疗所有角色。",
|
||||
"description": "治疗当前队伍的角色"
|
||||
},
|
||||
"help": {
|
||||
"usage": "用法:",
|
||||
"aliases": "别名:",
|
||||
"available_commands": "可用命令:",
|
||||
"description": "发送帮助信息或显示指定命令的信息"
|
||||
},
|
||||
"kick": {
|
||||
"player_kick_player": "玩家 [%s:%s] 已将 [%s:%s] 踢出。",
|
||||
"server_kick_player": "正在踢出玩家 [%s:%s]...",
|
||||
@ -220,6 +222,10 @@
|
||||
"success": "目前在线人数:%s",
|
||||
"description": "查看所有玩家"
|
||||
},
|
||||
"nostamina": {
|
||||
"success": "NoStamina 已设为 %s。[用户:%s]",
|
||||
"description": "保持你的体力处于最高状态"
|
||||
},
|
||||
"permission": {
|
||||
"usage": "用法:permission <add|remove> <用户名> <权限>",
|
||||
"add": "权限已添加。",
|
||||
@ -256,6 +262,9 @@
|
||||
"success": "重置完成。",
|
||||
"description": "重置指定玩家的商店刷新时间"
|
||||
},
|
||||
"restart": {
|
||||
"description": "重新启动服务器"
|
||||
},
|
||||
"sendMail": {
|
||||
"usage": "用法:sendmail <用户ID|all|help> [模板ID]",
|
||||
"user_not_exist": "用户 '%s' 不存在。",
|
||||
@ -368,29 +377,20 @@
|
||||
"success": "传送 %s 到坐标 %s, %s, %s,场景为 %s。",
|
||||
"description": "改变指定玩家的位置"
|
||||
},
|
||||
"unlimitenergy": {
|
||||
"success": "UnlimitEnergy 已设为 %s。[用户:%s]",
|
||||
"config_error": "命令不可用。因为 config.json 中 energyUsage 为 false。",
|
||||
"description": "使用元素爆发而不消耗能量"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "现已解锁深境回廊(1-8层)。",
|
||||
"description": "解锁深境螺旋"
|
||||
},
|
||||
"weather": {
|
||||
"usage": "用法:weather <气候类型(天气ID)> <天气类型(气候ID)>\n天气类型 0: 无, 1: 晴天, 2: 多云, 3: 雨, 4: 雷雨, 5: 雪, 6: 雾",
|
||||
"success": "已更改气候类型为 %s,天气类型为 %s。",
|
||||
"invalid_id": "无效的ID。",
|
||||
"description": "更改天气"
|
||||
},
|
||||
"drop": {
|
||||
"command_usage": "用法:drop <物品ID|物品名称> [数量]",
|
||||
"success": "已丢下 %s 个 %s。",
|
||||
"description": "在你附近丢下物品"
|
||||
},
|
||||
"help": {
|
||||
"usage": "用法:",
|
||||
"aliases": "别名:",
|
||||
"available_commands": "可用命令:",
|
||||
"description": "发送帮助信息或显示指定命令的信息"
|
||||
},
|
||||
"restart": {
|
||||
"description": "重新启动服务器"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "解锁完成。",
|
||||
"description": "解锁深境回廊(1-8层)"
|
||||
}
|
||||
},
|
||||
"gacha": {
|
||||
|
@ -389,8 +389,8 @@
|
||||
"description": "重新啟動伺服器。"
|
||||
},
|
||||
"unlocktower": {
|
||||
"success": "解鎖完成。",
|
||||
"description": "解鎖深境迴廊。"
|
||||
"success": "現已解鎖深境迴廊。",
|
||||
"description": "解鎖深境螺旋。"
|
||||
}
|
||||
},
|
||||
"gacha": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user