Merge branch 'development' into more-events

# Conflicts:
#	src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java
#	src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
#	src/main/java/emu/grasscutter/game/entity/GameEntity.java
#	src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java
This commit is contained in:
KingRainbow44
2022-07-22 17:52:58 -04:00
257 changed files with 17788 additions and 16667 deletions

View File

@@ -1,15 +0,0 @@
package emu.grasscutter.game.managers;
import emu.grasscutter.server.game.GameServer;
public class AccountManager {
private final GameServer server;
public AccountManager(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return server;
}
}

View File

@@ -1,112 +0,0 @@
package emu.grasscutter.game.managers;
import com.google.gson.reflect.TypeToken;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.DataLoader;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.AnnounceDataOuterClass;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketServerAnnounceNotify;
import emu.grasscutter.server.packet.send.PacketServerAnnounceRevokeNotify;
import emu.grasscutter.utils.Utils;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
import java.io.InputStreamReader;
import java.util.*;
@Getter
public class AnnouncementManager {
public final GameServer server;
public AnnouncementManager(GameServer server){
this.server = server;
loadConfig();
}
Map<Integer, AnnounceConfigItem> announceConfigItemMap = new HashMap<>();
private int loadConfig() {
try (var fileReader = new InputStreamReader(DataLoader.load("Announcement.json"))) {
List<AnnounceConfigItem> announceConfigItems = Grasscutter.getGsonFactory().fromJson(fileReader,
TypeToken.getParameterized(List.class, AnnounceConfigItem.class).getType());
announceConfigItemMap = new HashMap<>();
announceConfigItems.forEach(i -> announceConfigItemMap.put(i.getTemplateId(), i));
} catch (Exception e) {
Grasscutter.getLogger().error("Unable to load server announce config.", e);
}
return announceConfigItemMap.size();
}
public List<Player> getOnlinePlayers() {
return getServer().getWorlds().stream()
.map(World::getPlayers)
.flatMap(Collection::stream)
.toList();
}
public void broadcast(List<AnnounceConfigItem> tpl) {
if(tpl == null || tpl.size() == 0){
return;
}
var list = tpl.stream()
.map(AnnounceConfigItem::toProto)
.map(AnnounceDataOuterClass.AnnounceData.Builder::build)
.toList();
getOnlinePlayers().forEach(i -> i.sendPacket(new PacketServerAnnounceNotify(list)));
}
public int refresh() {
return loadConfig();
}
public void revoke(int tplId) {
getOnlinePlayers().forEach(i -> i.sendPacket(new PacketServerAnnounceRevokeNotify(tplId)));
}
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
public class AnnounceConfigItem{
int templateId;
AnnounceType type;
int frequency;
String content;
Date beginTime;
Date endTime;
boolean tick;
int interval;
public AnnounceDataOuterClass.AnnounceData.Builder toProto(){
var proto = AnnounceDataOuterClass.AnnounceData.newBuilder();
proto.setConfigId(templateId)
// I found the time here is useless
.setBeginTime(Utils.getCurrentSeconds() + 1)
.setEndTime(Utils.getCurrentSeconds() + 10);
if(type == AnnounceType.CENTER){
proto.setCenterSystemText(content)
.setCenterSystemFrequency(frequency)
;
}else{
proto.setCountDownText(content)
.setCountDownFrequency(frequency)
;
}
return proto;
}
}
public enum AnnounceType{
CENTER, COUNTDOWN
}
}

View File

@@ -10,8 +10,10 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.net.proto.CookRecipeDataOuterClass;
import emu.grasscutter.net.proto.PlayerCookArgsReqOuterClass.PlayerCookArgsReq;
import emu.grasscutter.net.proto.PlayerCookReqOuterClass.PlayerCookReq;
@@ -22,14 +24,12 @@ import emu.grasscutter.server.packet.send.PacketPlayerCookArgsRsp;
import emu.grasscutter.server.packet.send.PacketPlayerCookRsp;
import io.netty.util.internal.ThreadLocalRandom;
public class CookingManager {
public class CookingManager extends BasePlayerManager {
private static final int MANUAL_PERFECT_COOK_QUALITY = 3;
private static Set<Integer> defaultUnlockedRecipies;
private final Player player;
public CookingManager(Player player) {
this.player = player;
super(player);
}
public static void initialize() {
@@ -48,12 +48,12 @@ public class CookingManager {
********************/
public synchronized boolean unlockRecipe(GameItem recipeItem) {
// Make sure this is actually a cooking recipe.
if (!recipeItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) {
if (recipeItem.getItemData().getItemUse().get(0).getUseOp() != ItemUseOp.ITEM_USE_UNLOCK_COOK_RECIPE) {
return false;
}
// Determine the recipe we should unlock.
int recipeId = Integer.parseInt(recipeItem.getItemData().getItemUse().get(0).getUseParam().get(0));
int recipeId = Integer.parseInt(recipeItem.getItemData().getItemUse().get(0).getUseParam()[0]);
// Remove the item from the player's inventory.
// We need to do this here, before sending CookRecipeDataNotify, or the the UI won't correctly update.
@@ -103,9 +103,9 @@ public class CookingManager {
}
// Get result item information.
int qualityIndex =
quality == 0
? 2
int qualityIndex =
quality == 0
? 2
: quality - 1;
ItemParamData resultParam = recipeData.getQualityOutputVec().get(qualityIndex);

View File

@@ -4,7 +4,9 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.home.FurnitureMakeSlotItem;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.net.proto.ItemParamOuterClass;
import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
import emu.grasscutter.server.packet.send.*;
@@ -13,42 +15,42 @@ import emu.grasscutter.utils.Utils;
import java.util.ArrayList;
import java.util.List;
public class FurnitureManager {
private final Player player;
public class FurnitureManager extends BasePlayerManager {
public FurnitureManager(Player player) {
this.player = player;
super(player);
}
public void onLogin(){
public void onLogin() {
notifyUnlockFurniture();
notifyUnlockFurnitureSuite();
}
public void notifyUnlockFurniture(){
public void notifyUnlockFurniture() {
player.getSession().send(new PacketUnlockedFurnitureFormulaDataNotify(player.getUnlockedFurniture()));
}
public void notifyUnlockFurnitureSuite(){
public void notifyUnlockFurnitureSuite() {
player.getSession().send(new PacketUnlockedFurnitureSuiteDataNotify(player.getUnlockedFurnitureSuite()));
}
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem){
public synchronized boolean unlockFurnitureOrSuite(GameItem useItem) {
ItemUseOp itemUseOp = useItem.getItemData().getItemUse().get(0).getUseOp();
// Check
if (!List.of("ITEM_USE_UNLOCK_FURNITURE_FORMULA", "ITEM_USE_UNLOCK_FURNITURE_SUITE")
.contains(useItem.getItemData().getItemUse().get(0).getUseOp())) {
if (itemUseOp != ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_SUITE && itemUseOp != ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_FORMULA) {
return false;
}
int furnitureIdOrSuiteId = Integer.parseInt(useItem.getItemData().getItemUse().get(0).getUseParam().get(0));
int furnitureIdOrSuiteId = Integer.parseInt(useItem.getItemData().getItemUse().get(0).getUseParam()[0]);
// Remove first
player.getInventory().removeItem(useItem, 1);
if("ITEM_USE_UNLOCK_FURNITURE_FORMULA".equals(useItem.getItemData().getItemUse().get(0).getUseOp())){
if (useItem.getItemData().getItemUse().get(0).getUseOp() == ItemUseOp.ITEM_USE_UNLOCK_FURNITURE_FORMULA) {
player.getUnlockedFurniture().add(furnitureIdOrSuiteId);
notifyUnlockFurniture();
}else{
}else {
player.getUnlockedFurnitureSuite().add(furnitureIdOrSuiteId);
notifyUnlockFurnitureSuite();
}
@@ -57,19 +59,19 @@ public class FurnitureManager {
public void startMake(int makeId, int avatarId) {
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
if(makeData == null){
if (makeData == null) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, null));
return;
}
// check slot count
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()){
if (player.getHome().getLevelData().getFurnitureMakeSlotCount() <= player.getHome().getFurnitureMakeSlotItemList().size()) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_FURNITURE_MAKE_SLOT_FULL_VALUE, null));
return;
}
// pay items first
if(!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))){
if (!player.getInventory().payItems(makeData.getMaterialItems().toArray(new ItemParamData[0]))) {
player.getSession().send(new PacketFurnitureMakeStartRsp(Retcode.RET_HOME_FURNITURE_COUNT_NOT_ENOUGH_VALUE, null));
return;
}
@@ -93,7 +95,7 @@ public class FurnitureManager {
}
public void queryStatus() {
if (player.getHome().getFurnitureMakeSlotItemList() == null){
if (player.getHome().getFurnitureMakeSlotItemList() == null) {
player.getHome().setFurnitureMakeSlotItemList(new ArrayList<>());
}
@@ -103,7 +105,7 @@ public class FurnitureManager {
public void take(int index, int makeId, boolean isFastFinish) {
var makeData = GameData.getFurnitureMakeConfigDataMap().get(makeId);
if(makeData == null){
if (makeData == null) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_CONFIG_ERROR_VALUE, makeId, null, null));
return;
}
@@ -112,19 +114,19 @@ public class FurnitureManager {
.filter(x -> x.getIndex() == index && x.getMakeId() == makeId)
.findFirst();
if(slotItem.isEmpty()){
if (slotItem.isEmpty()) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_NO_MAKE_DATA_VALUE, makeId, null, null));
return;
}
// pay the speedup item
if(isFastFinish && !player.getInventory().payItem(107013,1)){
if (isFastFinish && !player.getInventory().payItem(107013,1)) {
player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
return;
}
// check if player can take
// if(slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish){
// if (slotItem.get().getBeginTime() + slotItem.get().getDurTime() >= Utils.getCurrentSeconds() && !isFastFinish) {
// player.getSession().send(new PacketTakeFurnitureMakeRsp(Retcode.RET_FURNITURE_MAKE_UNFINISH_VALUE, makeId, null, null));
// return;
// }

View File

@@ -1,44 +0,0 @@
package emu.grasscutter.game.managers;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.EntityVehicle;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
public record InsectCaptureManager(Player player) {
public void arrestSmallCreature(GameEntity entity) {
//System.out.println("arrestSmallCreature!");
EnvAnimalGatherConfigData gather;
int thingId;
if (entity instanceof EntityMonster monster) {
thingId = monster.getMonsterData().getId();
gather = GameData.getEnvAnimalGatherConfigDataMap().get(thingId);
} else if (entity instanceof EntityVehicle gadget) {
thingId = gadget.getGadgetId();
gather = GameData.getEnvAnimalGatherConfigDataMap().get(thingId);
} else {
return;
}
if (gather == null) {
Grasscutter.getLogger().warn("monster/gather(id={}) couldn't be caught.", thingId);
return;
}
String type = gather.getEntityType();
if ((type.equals("Monster") && entity instanceof EntityMonster) || (type.equals("Gadget") && entity instanceof EntityVehicle)) {
EnvAnimalGatherConfigData.GatherItem gatherItem = gather.gatherItem();
ItemData data = GameData.getItemDataMap().get(gatherItem.getId());
GameItem item = new GameItem(data, gatherItem.getCount());
player.getInventory().addItem(item, ActionReason.SubfieldDrop);
entity.getScene().removeEntity(entity, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE);
} else {
Grasscutter.getLogger().warn("monster/gather(id={}) has a wrong type.", thingId);
}
}
}

View File

@@ -1,961 +0,0 @@
package emu.grasscutter.game.managers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.binout.OpenConfigEntry;
import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.AvatarPromoteData;
import emu.grasscutter.data.excels.AvatarSkillData;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
import emu.grasscutter.data.excels.AvatarTalentData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.ProudSkillData;
import emu.grasscutter.data.excels.WeaponPromoteData;
import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens;
import emu.grasscutter.game.avatar.Avatar;
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.shop.ShopChestBatchUseTable;
import emu.grasscutter.game.shop.ShopChestTable;
import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
public class InventoryManager {
private final GameServer server;
private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction
private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence
private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction
private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence
private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore
private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore
private final static int WEAPON_ORE_3 = 104013; // Mystic Enhancement Ore
private final static int WEAPON_ORE_EXP_1 = 400; // Enhancement Ore
private final static int WEAPON_ORE_EXP_2 = 2000; // Fine Enhancement Ore
private final static int WEAPON_ORE_EXP_3 = 10000; // Mystic Enhancement Ore
private final static int AVATAR_BOOK_1 = 104001; // Wanderer's Advice
private final static int AVATAR_BOOK_2 = 104002; // Adventurer's Experience
private final static int AVATAR_BOOK_3 = 104003; // Hero's Wit
private final static int AVATAR_BOOK_EXP_1 = 1000; // Wanderer's Advice
private final static int AVATAR_BOOK_EXP_2 = 5000; // Adventurer's Experience
private final static int AVATAR_BOOK_EXP_3 = 20000; // Hero's Wit
public InventoryManager(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return server;
}
public void lockEquip(Player player, long targetEquipGuid, boolean isLocked) {
GameItem equip = player.getInventory().getItemByGuid(targetEquipGuid);
if (equip == null || !equip.getItemData().isEquip()) {
return;
}
equip.setLocked(isLocked);
equip.save();
player.sendPacket(new PacketStoreItemChangeNotify(equip));
player.sendPacket(new PacketSetEquipLockStateRsp(equip));
}
public void upgradeRelic(Player player, long targetGuid, List<Long> foodRelicList, List<ItemParam> list) {
GameItem relic = player.getInventory().getItemByGuid(targetGuid);
if (relic == null || relic.getItemType() != ItemType.ITEM_RELIQUARY) {
return;
}
int moraCost = 0;
int expGain = 0;
List<GameItem> foodRelics = new ArrayList<GameItem>();
for (long guid : foodRelicList) {
// Add to delete queue
GameItem food = player.getInventory().getItemByGuid(guid);
if (food == null || !food.isDestroyable()) {
continue;
}
// Calculate mora cost
moraCost += food.getItemData().getBaseConvExp();
expGain += food.getItemData().getBaseConvExp();
// Feeding artifact with exp already
if (food.getTotalExp() > 0) {
expGain += (food.getTotalExp() * 4) / 5;
}
foodRelics.add(food);
}
List<ItemParamData> payList = new ArrayList<ItemParamData>();
for (ItemParam itemParam : list) {
int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
int gain = amount * switch(itemParam.getItemId()) {
case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1;
case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2;
default -> 0;
};
expGain += gain;
moraCost += gain;
payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount()));
}
// Make sure exp gain is valid
if (expGain <= 0) {
return;
}
// Confirm payment of materials and mora (assume food relics are payable afterwards)
payList.add(new ItemParamData(202, moraCost));
if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) {
return;
}
// Consume food relics
player.getInventory().removeItems(foodRelics);
// Implement random rate boost
int rate = 1;
int boost = Utils.randomRange(1, 100);
if (boost == 100) {
rate = 5;
} else if (boost <= 9) {
rate = 2;
}
expGain *= rate;
// Now we upgrade
int level = relic.getLevel();
int oldLevel = level;
int exp = relic.getExp();
int totalExp = relic.getTotalExp();
int reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
int upgrades = 0;
List<Integer> oldAppendPropIdList = new ArrayList<>(relic.getAppendPropIdList());
while (expGain > 0 && reqExp > 0 && level < relic.getItemData().getMaxLevel()) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
totalExp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// On relic levelup
if (relic.getItemData().getAddPropLevelSet() != null && relic.getItemData().getAddPropLevelSet().contains(level)) {
upgrades += 1;
}
// Set req exp
reqExp = GameData.getRelicExpRequired(relic.getItemData().getRankLevel(), level);
}
}
relic.addAppendProps(upgrades);
// Save
relic.setLevel(level);
relic.setExp(exp);
relic.setTotalExp(totalExp);
relic.save();
// Avatar
if (oldLevel != level) {
Avatar avatar = relic.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(relic.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
}
// Packet
player.sendPacket(new PacketStoreItemChangeNotify(relic));
player.sendPacket(new PacketReliquaryUpgradeRsp(relic, rate, oldLevel, oldAppendPropIdList));
}
public List<ItemParam> calcWeaponUpgradeReturnItems(Player player, long targetGuid, List<Long> foodWeaponGuidList, List<ItemParam> itemParamList) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
// Sanity checks
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return null;
}
WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
if (promoteData == null) {
return null;
}
// Get exp gain
int expGain = 0;
for (long guid : foodWeaponGuidList) {
GameItem food = player.getInventory().getItemByGuid(guid);
if (food == null) {
continue;
}
expGain += food.getItemData().getWeaponBaseExp();
if (food.getTotalExp() > 0) {
expGain += (food.getTotalExp() * 4) / 5;
}
}
for (ItemParam param : itemParamList) {
expGain += param.getCount() * switch(param.getItemId()) {
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
default -> 0;
};
}
// Try
int maxLevel = promoteData.getUnlockMaxLevel();
int level = weapon.getLevel();
int exp = weapon.getExp();
int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
}
}
return getLeftoverOres(expGain);
}
public void upgradeWeapon(Player player, long targetGuid, List<Long> foodWeaponGuidList, List<ItemParam> itemParamList) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
// Sanity checks
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return;
}
WeaponPromoteData promoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
if (promoteData == null) {
return;
}
// Get exp gain
int expGain = 0, expGainFree = 0;
List<GameItem> foodWeapons = new ArrayList<GameItem>();
for (long guid : foodWeaponGuidList) {
GameItem food = player.getInventory().getItemByGuid(guid);
if (food == null || !food.isDestroyable()) {
continue;
}
expGain += food.getItemData().getWeaponBaseExp();
if (food.getTotalExp() > 0) {
expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D
}
foodWeapons.add(food);
}
List<ItemParamData> payList = new ArrayList<ItemParamData>();
for (ItemParam param : itemParamList) {
int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
int gain = amount * switch(param.getItemId()) {
case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1;
case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2;
case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3;
default -> 0;
};
expGain += gain;
payList.add(new ItemParamData(param.getItemId(), amount));
}
// Make sure exp gain is valid
int moraCost = expGain / 10;
expGain += expGainFree;
if (expGain <= 0) {
return;
}
// Confirm payment of materials and mora (assume food weapons are payable afterwards)
payList.add(new ItemParamData(202, moraCost));
if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) {
return;
}
player.getInventory().removeItems(foodWeapons);
// Level up
int maxLevel = promoteData.getUnlockMaxLevel();
int level = weapon.getLevel();
int oldLevel = level;
int exp = weapon.getExp();
int totalExp = weapon.getTotalExp();
int reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
totalExp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getWeaponExpRequired(weapon.getItemData().getRankLevel(), level);
}
}
List<ItemParam> leftovers = getLeftoverOres(expGain);
player.getInventory().addItemParams(leftovers);
weapon.setLevel(level);
weapon.setExp(exp);
weapon.setTotalExp(totalExp);
weapon.save();
// Avatar
if (oldLevel != level) {
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers));
}
private List<ItemParam> getLeftoverOres(int leftover) {
List<ItemParam> leftoverOreList = new ArrayList<>(3);
if (leftover < WEAPON_ORE_EXP_1) {
return leftoverOreList;
}
// Get leftovers
int ore3 = leftover / WEAPON_ORE_EXP_3;
leftover = leftover % WEAPON_ORE_EXP_3;
int ore2 = leftover / WEAPON_ORE_EXP_2;
leftover = leftover % WEAPON_ORE_EXP_2;
int ore1 = leftover / WEAPON_ORE_EXP_1;
if (ore3 > 0) {
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build());
} if (ore2 > 0) {
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_2).setCount(ore2).build());
} if (ore1 > 0) {
leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_1).setCount(ore1).build());
}
return leftoverOreList;
}
public void refineWeapon(Player player, long targetGuid, long feedGuid) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
GameItem feed = player.getInventory().getItemByGuid(feedGuid);
// Sanity checks
if (weapon == null || feed == null || !feed.isDestroyable()) {
return;
}
if (weapon.getItemData().getAwakenMaterial() == 0) {
if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemId() != feed.getItemId()) {
return;
}
} else {
if (weapon.getItemType() != ItemType.ITEM_WEAPON || weapon.getItemData().getAwakenMaterial() != feed.getItemId()) {
return;
}
}
if (weapon.getRefinement() >= 4 || weapon.getAffixes() == null || weapon.getAffixes().size() == 0) {
return;
}
// Calculate
int oldRefineLevel = weapon.getRefinement();
int targetRefineLevel = Math.min(oldRefineLevel + feed.getRefinement() + 1, 4);
int moraCost = 0;
try {
moraCost = weapon.getItemData().getAwakenCosts()[weapon.getRefinement()];
} catch (Exception e) {
return;
}
// Mora check
if (player.getMora() >= moraCost) {
player.setMora(player.getMora() - moraCost);
} else {
return;
}
// Consume weapon
player.getInventory().removeItem(feed, 1);
// Get
weapon.setRefinement(targetRefineLevel);
weapon.save();
// Avatar
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponAwakenRsp(avatar, weapon, feed, oldRefineLevel));
}
public void promoteWeapon(Player player, long targetGuid) {
GameItem weapon = player.getInventory().getItemByGuid(targetGuid);
if (weapon == null || weapon.getItemType() != ItemType.ITEM_WEAPON) {
return;
}
int nextPromoteLevel = weapon.getPromoteLevel() + 1;
WeaponPromoteData currentPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), weapon.getPromoteLevel());
WeaponPromoteData nextPromoteData = GameData.getWeaponPromoteData(weapon.getItemData().getWeaponPromoteId(), nextPromoteLevel);
if (currentPromoteData == null || nextPromoteData == null) {
return;
}
// Level check
if (weapon.getLevel() != currentPromoteData.getUnlockMaxLevel()) {
return;
}
// Pay materials and mora if possible
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
if (nextPromoteData.getCoinCost() > 0) {
costs = Arrays.copyOf(costs, costs.length + 1);
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
}
if (!player.getInventory().payItems(costs)) {
return;
}
int oldPromoteLevel = weapon.getPromoteLevel();
weapon.setPromoteLevel(nextPromoteLevel);
weapon.save();
// Avatar
Avatar avatar = weapon.getEquipCharacter() > 0 ? player.getAvatars().getAvatarById(weapon.getEquipCharacter()) : null;
if (avatar != null) {
avatar.recalcStats();
}
// Packets
player.sendPacket(new PacketStoreItemChangeNotify(weapon));
player.sendPacket(new PacketWeaponPromoteRsp(weapon, oldPromoteLevel));
}
public void promoteAvatar(Player player, long guid) {
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
// Sanity checks
if (avatar == null) {
return;
}
int nextPromoteLevel = avatar.getPromoteLevel() + 1;
AvatarPromoteData currentPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
AvatarPromoteData nextPromoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), nextPromoteLevel);
if (currentPromoteData == null || nextPromoteData == null) {
return;
}
// Level check
if (avatar.getLevel() != currentPromoteData.getUnlockMaxLevel()) {
return;
}
// Pay materials and mora if possible
ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null?
if (nextPromoteData.getCoinCost() > 0) {
costs = Arrays.copyOf(costs, costs.length + 1);
costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost());
}
if (!player.getInventory().payItems(costs)) {
return;
}
// Update promote level
avatar.setPromoteLevel(nextPromoteLevel);
// Update proud skills
AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId());
if (skillDepot != null && skillDepot.getInherentProudSkillOpens() != null) {
for (InherentProudSkillOpens openData : skillDepot.getInherentProudSkillOpens()) {
if (openData.getProudSkillGroupId() == 0) {
continue;
}
if (openData.getNeedAvatarPromoteLevel() == avatar.getPromoteLevel()) {
int proudSkillId = (openData.getProudSkillGroupId() * 100) + 1;
if (GameData.getProudSkillDataMap().containsKey(proudSkillId)) {
avatar.getProudSkillList().add(proudSkillId);
player.sendPacket(new PacketProudSkillChangeNotify(avatar));
}
}
}
}
// Packets
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarPromoteRsp(avatar));
// TODO Send entity prop update packet to world
avatar.recalcStats(true);
avatar.save();
}
public void upgradeAvatar(Player player, long guid, int itemId, int count) {
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
// Sanity checks
if (avatar == null) {
return;
}
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
if (promoteData == null) {
return;
}
// Calc exp
int expGain = switch(itemId) {
case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count;
case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count;
case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count;
default -> 0;
};
// Sanity check
if (expGain <= 0) {
return;
}
// Payment check
int moraCost = expGain / 5;
ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)};
if (!player.getInventory().payItems(costItems)) {
return;
}
// Level up
upgradeAvatar(player, avatar, promoteData, expGain);
}
public void upgradeAvatar(Player player, Avatar avatar, int expGain) {
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(avatar.getAvatarData().getAvatarPromoteId(), avatar.getPromoteLevel());
if (promoteData == null) {
return;
}
upgradeAvatar(player, avatar, promoteData, expGain);
}
public void upgradeAvatar(Player player, Avatar avatar, AvatarPromoteData promoteData, int expGain) {
int maxLevel = promoteData.getUnlockMaxLevel();
int level = avatar.getLevel();
int oldLevel = level;
int exp = avatar.getExp();
int reqExp = GameData.getAvatarLevelExpRequired(level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
// Do calculations
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
// Level up
if (exp >= reqExp) {
// Exp
exp = 0;
level += 1;
// Set req exp
reqExp = GameData.getAvatarLevelExpRequired(level);
}
}
// Old map for packet
Map<Integer, Float> oldPropMap = avatar.getFightProperties();
if (oldLevel != level) {
// Deep copy if level has changed
oldPropMap = avatar.getFightProperties().int2FloatEntrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
// Done
avatar.setLevel(level);
avatar.setExp(exp);
avatar.recalcStats();
avatar.save();
// TODO Send entity prop update packet to world
// Packets
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap));
}
public void upgradeAvatarFetterLevel(Player player, Avatar avatar, int expGain) {
// May work. Not test.
int maxLevel = 10; // Keep it until I think of a more "elegant" way
int level = avatar.getFetterLevel();
int exp = avatar.getFetterExp();
int reqExp = GameData.getAvatarFetterLevelExpRequired(level);
while (expGain > 0 && reqExp > 0 && level < maxLevel) {
int toGain = Math.min(expGain, reqExp - exp);
exp += toGain;
expGain -= toGain;
if (exp >= reqExp) {
exp = 0;
level += 1;
reqExp = GameData.getAvatarFetterLevelExpRequired(level);
}
}
avatar.setFetterLevel(level);
avatar.setFetterExp(exp);
avatar.save();
player.sendPacket(new PacketAvatarPropNotify(avatar));
player.sendPacket(new PacketAvatarFetterDataNotify(avatar));
}
public void upgradeAvatarSkill(Player player, long guid, int skillId) {
// Sanity checks
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) {
return;
}
// Make sure avatar has skill
if (!avatar.getSkillLevelMap().containsKey(skillId)) {
return;
}
AvatarSkillData skillData = GameData.getAvatarSkillDataMap().get(skillId);
if (skillData == null) {
return;
}
// Get data for next skill level
int currentLevel = avatar.getSkillLevelMap().get(skillId);
int nextLevel = currentLevel + 1;
int proudSkillId = (skillData.getProudSkillGroupId() * 100) + nextLevel;
// Capped at level 10 talent
if (nextLevel > 10) {
return;
}
// Proud skill data
ProudSkillData proudSkill = GameData.getProudSkillDataMap().get(proudSkillId);
if (proudSkill == null) {
return;
}
// Make sure break level is correct
if (avatar.getPromoteLevel() < proudSkill.getBreakLevel()) {
return;
}
// Pay materials and mora if possible
List<ItemParamData> costs = new ArrayList<ItemParamData>(proudSkill.getCostItems()); // Can this be null?
if (proudSkill.getCoinCost() > 0) {
costs.add(new ItemParamData(202, proudSkill.getCoinCost()));
}
if (!player.getInventory().payItems(costs.toArray(new ItemParamData[0]))) {
return;
}
// Upgrade skill
avatar.getSkillLevelMap().put(skillId, nextLevel);
avatar.save();
// Packet
player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel));
player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel));
}
public void unlockAvatarConstellation(Player player, long guid) {
// Sanity checks
Avatar avatar = player.getAvatars().getAvatarByGuid(guid);
if (avatar == null) {
return;
}
// Get talent
int currentTalentLevel = avatar.getCoreProudSkillLevel();
int nextTalentId = ((avatar.getAvatarId() % 10000000) * 10) + currentTalentLevel + 1;
if (avatar.getAvatarId() == 10000006) {
// Lisa is special in that her talentId starts with 4 instead of 6.
nextTalentId = 40 + currentTalentLevel + 1;
}
AvatarTalentData talentData = GameData.getAvatarTalentDataMap().get(nextTalentId);
if (talentData == null) {
return;
}
// Pay constellation item if possible
if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) {
return;
}
// Apply + recalc
avatar.getTalentIdList().add(talentData.getId());
avatar.setCoreProudSkillLevel(currentTalentLevel + 1);
// Packet
player.sendPacket(new PacketAvatarUnlockTalentNotify(avatar, nextTalentId));
player.sendPacket(new PacketUnlockAvatarTalentRsp(avatar, nextTalentId));
// Proud skill bonus map (Extra skills)
OpenConfigEntry entry = GameData.getOpenConfigEntries().get(talentData.getOpenConfig());
if (entry != null) {
if (entry.getExtraTalentIndex() > 0) {
// Check if new constellation adds +3 to a skill level
avatar.recalcConstellations();
// Packet
player.sendPacket(new PacketProudSkillExtraLevelNotify(avatar, entry.getExtraTalentIndex()));
} else if (entry.getSkillPointModifiers() != null) {
// Check if new constellation adds skill charges
avatar.recalcConstellations();
// Packet
for (SkillPointModifier mod : entry.getSkillPointModifiers()) {
player.sendPacket(
new PacketAvatarSkillMaxChargeCountNotify(avatar, mod.getSkillId(), avatar.getSkillExtraChargeMap().getOrDefault(mod.getSkillId(), 0))
);
}
}
}
// Recalc + save avatar
avatar.recalcStats(true);
avatar.save();
}
public void destroyMaterial(Player player, List<MaterialInfo> list) {
// Return materials
Int2IntOpenHashMap returnMaterialMap = new Int2IntOpenHashMap();
for (MaterialInfo info : list) {
// Sanity check
if (info.getCount() <= 0) {
continue;
}
GameItem item = player.getInventory().getItemByGuid(info.getGuid());
if (item == null || !item.isDestroyable()) {
continue;
}
// Remove
int removeAmount = Math.min(info.getCount(), item.getCount());
player.getInventory().removeItem(item, removeAmount);
// Delete material return items
if (item.getItemData().getDestroyReturnMaterial().length > 0) {
for (int i = 0; i < item.getItemData().getDestroyReturnMaterial().length; i++) {
returnMaterialMap.addTo(item.getItemData().getDestroyReturnMaterial()[i], item.getItemData().getDestroyReturnMaterialCount()[i]);
}
}
}
// Give back items
if (returnMaterialMap.size() > 0) {
for (Int2IntMap.Entry e : returnMaterialMap.int2IntEntrySet()) {
player.getInventory().addItem(new GameItem(e.getIntKey(), e.getIntValue()));
}
}
// Packets
player.sendPacket(new PacketDestroyMaterialRsp(returnMaterialMap));
}
public GameItem useItem(Player player, long targetGuid, long itemGuid, int count, int optionId) {
Avatar target = player.getAvatars().getAvatarByGuid(targetGuid);
GameItem useItem = player.getInventory().getItemByGuid(itemGuid);
if (useItem == null) {
return null;
}
int used = 0;
boolean useSuccess = false;
// Use
switch (useItem.getItemData().getMaterialType()) {
case MATERIAL_FOOD:
if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_DEAD_AVATAR")) {
if (target == null) {
break;
}
used = player.getTeamManager().reviveAvatar(target) ? 1 : 0;
}
break;
case MATERIAL_NOTICE_ADD_HP:
if (useItem.getItemData().getUseTarget().equals("ITEM_USE_TARGET_SPECIFY_ALIVE_AVATAR")) {
if (target == null) {
break;
}
int[] SatiationParams = useItem.getItemData().getSatiationParams();
used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0;
}
break;
case MATERIAL_CONSUME:
// Make sure we have usage data for this material.
if (useItem.getItemData().getItemUse() == null) {
break;
}
// Handle forging blueprints.
if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
// 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);
}
// Handle cooking recipies.
if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) {
// Unlock.
useSuccess = player.getCookingManager().unlockRecipe(useItem);
}
break;
case MATERIAL_FURNITURE_FORMULA:
case MATERIAL_FURNITURE_SUITE_FORMULA:
if (useItem.getItemData().getItemUse() == null) {
break;
}
useSuccess = player.getFurnitureManager().unlockFurnitureOrSuite(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();
List<GameItem> rewardItemList = new ArrayList<>();
for (ShopChestTable shopChestTable : shopChestTableList) {
if (shopChestTable.getItemId() != useItem.getItemId()) {
continue;
}
if (shopChestTable.getContainsItem() == null) {
break;
}
for (ItemParamData itemParamData : shopChestTable.getContainsItem()) {
ItemData itemData = GameData.getItemDataMap().get(itemParamData.getId());
if (itemData == null) {
continue;
}
rewardItemList.add(new GameItem(itemData, itemParamData.getCount()));
}
if (!rewardItemList.isEmpty()) {
player.getInventory().addItems(rewardItemList, ActionReason.Shop);
}
used = 1;
break;
}
break;
case MATERIAL_CHEST_BATCH_USE:
if (optionId < 1) {
break;
}
List<ShopChestBatchUseTable> shopChestBatchUseTableList = player.getServer().getShopManager().getShopChestBatchUseData();
for (ShopChestBatchUseTable shopChestBatchUseTable : shopChestBatchUseTableList) {
if (shopChestBatchUseTable.getItemId() != useItem.getItemId()) {
continue;
}
if (shopChestBatchUseTable.getOptionItem() == null || optionId > shopChestBatchUseTable.getOptionItem().size()) {
break;
}
int optionItemId = shopChestBatchUseTable.getOptionItem().get(optionId - 1);
ItemData itemData = GameData.getItemDataMap().get(optionItemId);
if (itemData == null) {
break;
}
player.getInventory().addItem(new GameItem(itemData, count), ActionReason.Shop);
used = count;
break;
}
break;
default:
break;
}
// Welkin
if (useItem.getItemId() == 1202) {
player.rechargeMoonCard();
used = 1;
}
// If we used at least one item, or one of the methods called here reports using the item successfully,
// we return the item to make UseItemRsp a success.
if (used > 0) {
player.getInventory().removeItem(useItem, used);
return useItem;
}
if (useSuccess) {
return useItem;
}
return null;
}
}

View File

@@ -1,159 +0,0 @@
package emu.grasscutter.game.managers;
import emu.grasscutter.game.CoopRequest;
import emu.grasscutter.game.props.EnterReason;
import emu.grasscutter.game.world.World;
import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType;
import emu.grasscutter.net.proto.PlayerApplyEnterMpReasonOuterClass.PlayerApplyEnterMpReason;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.Player.SceneLoadState;
import emu.grasscutter.net.proto.PlayerApplyEnterMpResultNotifyOuterClass;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpNotify;
import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultNotify;
import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
public class MultiplayerManager {
private final GameServer server;
public MultiplayerManager(GameServer server) {
this.server = server;
}
public GameServer getServer() {
return server;
}
public void applyEnterMp(Player player, int targetUid) {
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
// Sanity checks - Dont let player join if already in multiplayer
if (player.getWorld().isMultiplayer()) {
return;
}
/*
if (target.getWorld().isDungeon()) {
player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpReason.SceneCannotEnter));
return;
}
*/
// Get request
CoopRequest request = target.getCoopRequests().get(player.getUid());
if (request != null && !request.isExpired()) {
// Join request already exists
return;
}
// Put request in
request = new CoopRequest(player);
target.getCoopRequests().put(player.getUid(), request);
// Packet
target.sendPacket(new PacketPlayerApplyEnterMpNotify(player));
}
public void applyEnterMpReply(Player hostPlayer, int applyUid, boolean isAgreed) {
// Checks
CoopRequest request = hostPlayer.getCoopRequests().get(applyUid);
if (request == null || request.isExpired()) {
return;
}
// Remove now that we are handling it
Player requester = request.getRequester();
hostPlayer.getCoopRequests().remove(applyUid);
// Sanity checks - Dont let the requesting player join if they are already in multiplayer
if (requester.getWorld().isMultiplayer()) {
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP));
return;
}
// Response packet
request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE));
// Declined
if (!isAgreed) {
return;
}
// Success
if (!hostPlayer.getWorld().isMultiplayer()) {
// Player not in multiplayer, create multiplayer world
World world = new World(hostPlayer, true);
// Add
world.addPlayer(hostPlayer);
// Rejoin packet
hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPos()));
}
// Set scene pos and id of requester to the host player's
requester.getPos().set(hostPlayer.getPos());
requester.getRotation().set(hostPlayer.getRotation());
requester.setSceneId(hostPlayer.getSceneId());
// Make requester join
hostPlayer.getWorld().addPlayer(requester);
// Packet
requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPos()));
}
public boolean leaveCoop(Player player) {
// Make sure player's world is multiplayer
if (!player.getWorld().isMultiplayer()) {
return false;
}
// Make sure everyone's scene is loaded
for (Player p : player.getWorld().getPlayers()) {
if (p.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
}
// Create new world for player
World world = new World(player);
world.addPlayer(player);
// Packet
player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPos()));
return true;
}
public boolean kickPlayer(Player player, int targetUid) {
// Make sure player's world is multiplayer and that player is owner
if (!player.getWorld().isMultiplayer() || player.getWorld().getHost() != player) {
return false;
}
// Get victim and sanity checks
Player victim = player.getServer().getPlayerByUid(targetUid);
if (victim == null || victim == player) {
return false;
}
// Make sure victim's scene has loaded
if (victim.getSceneLoadState() != SceneLoadState.LOADED) {
return false;
}
// Kick
World world = new World(victim);
world.addPlayer(victim);
victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPos()));
return true;
}
}

View File

@@ -1,5 +1,8 @@
package emu.grasscutter.game.managers;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType;
@@ -7,13 +10,10 @@ 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 class ResinManager extends BasePlayerManager {
public ResinManager(Player player) {
this.player = player;
super(player);
}
/********************
@@ -26,7 +26,7 @@ public class ResinManager {
}
int currentResin = this.player.getProperty(PlayerProperty.PROP_PLAYER_RESIN);
// Check if the player has sufficient resin.
if (currentResin < amount) {
return false;
@@ -39,16 +39,16 @@ public class ResinManager {
// 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();
int currentTime = Utils.getCurrentSeconds();
this.player.setNextResinRefresh(currentTime + GAME_OPTIONS.resinOptions.rechargeTime);
}
// Send packets.
this.player.sendPacket(new PacketResinChangeNotify(this.player));
// Battle Pass trigger
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_COST_MATERIAL, 106, amount); // Resin item id = 106
return true;
}
@@ -122,7 +122,7 @@ public class ResinManager {
* Player login.
********************/
public synchronized void onPlayerLogin() {
// If resin usage is disabled, set resin to cap.
// 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);
@@ -132,7 +132,7 @@ public class ResinManager {
// 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);
}

View File

@@ -1,191 +1,191 @@
package emu.grasscutter.game.managers;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
// Statue of the Seven Manager
public class SotSManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private final Player player;
private final Logger logger = Grasscutter.getLogger();
private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false;
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
public SotSManager(Player player) {
this.player = player;
}
public boolean getIsAutoRecoveryEnabled() {
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
}
public void setIsAutoRecoveryEnabled(boolean enabled) {
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
player.save();
}
public int getAutoRecoveryPercentage() {
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
}
public void setAutoRecoveryPercentage(int percentage) {
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
player.save();
}
public long getLastUsed() {
return player.getSpringLastUsed();
}
public void setLastUsed() {
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
player.save();
}
public int getMaxVolume() {
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
}
public void setMaxVolume(int volume) {
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
player.save();
}
public int getCurrentVolume() {
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
}
public void setCurrentVolume(int volume) {
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
setLastUsed();
player.save();
}
public void handleEnterTransPointRegionNotify() {
logger.trace("Player entered statue region");
autoRevive();
if (autoRecoverTimer == null) {
autoRecoverTimer = new Timer();
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
}
}
public void handleExitTransPointRegionNotify() {
logger.trace("Player left statue region");
if (autoRecoverTimer != null) {
autoRecoverTimer.cancel();
autoRecoverTimer = null;
}
}
// autoRevive automatically revives all team members.
public void autoRevive() {
player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive();
if (isAlive) {
return;
}
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
player.getTeamManager().reviveAvatar(entity.getAvatar());
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
});
}
private class AutoRecoverTimerTick extends TimerTask {
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public void run() {
refillSpringVolume();
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
if (getIsAutoRecoveryEnabled()) {
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
if (priorityIndex >= 0) {
checkAndHealAvatar(activeTeam.get(priorityIndex));
}
for (int i = 0; i < activeTeam.size(); i++) {
if (i != priorityIndex) {
checkAndHealAvatar(activeTeam.get(i));
}
}
}
}
}
public void checkAndHealAvatar(EntityAvatar entity) {
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
if (currentHP == maxHP) {
return;
}
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
if (targetHP > currentHP) {
int needHP = targetHP - currentHP;
int currentVolume = getCurrentVolume();
if (currentVolume >= needHP) {
// sufficient
setCurrentVolume(currentVolume - needHP);
} else {
// insufficient balance
needHP = currentVolume;
setCurrentVolume(0);
}
if (needHP > 0) {
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
ChangeHpReason.CHANGE_HP_REASON_CHANGE_HP_ADD_STATUE));
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
}
}
}
public void refillSpringVolume() {
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
setMaxVolume(8500000);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage(100);
setIsAutoRecoveryEnabled(true);
int maxVolume = getMaxVolume();
int currentVolume = getCurrentVolume();
if (currentVolume < maxVolume) {
long now = System.currentTimeMillis() / 1000;
int secondsSinceLastUsed = (int) (now - getLastUsed());
// 15s = 1% max volume
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
logger.trace("Statue remaining HP volume: " + currentVolume);
setCurrentVolume(currentVolume);
}
}
}
package emu.grasscutter.game.managers;
import ch.qos.logback.classic.Logger;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
// Statue of the Seven Manager
public class SotSManager extends BasePlayerManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private final Logger logger = Grasscutter.getLogger();
private Timer autoRecoverTimer;
private final boolean enablePriorityHealing = false;
public final static int GlobalMaximumSpringVolume = PlayerProperty.PROP_MAX_SPRING_VOLUME.getMax();
public SotSManager(Player player) {
super(player);
}
public boolean getIsAutoRecoveryEnabled() {
return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1;
}
public void setIsAutoRecoveryEnabled(boolean enabled) {
player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0);
player.save();
}
public int getAutoRecoveryPercentage() {
return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT);
}
public void setAutoRecoveryPercentage(int percentage) {
player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage);
player.save();
}
public long getLastUsed() {
return player.getSpringLastUsed();
}
public void setLastUsed() {
player.setSpringLastUsed(System.currentTimeMillis() / 1000);
player.save();
}
public int getMaxVolume() {
return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME);
}
public void setMaxVolume(int volume) {
player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume);
player.save();
}
public int getCurrentVolume() {
return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME);
}
public void setCurrentVolume(int volume) {
player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume);
setLastUsed();
player.save();
}
public void handleEnterTransPointRegionNotify() {
logger.trace("Player entered statue region");
autoRevive();
if (autoRecoverTimer == null) {
autoRecoverTimer = new Timer();
autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000);
}
}
public void handleExitTransPointRegionNotify() {
logger.trace("Player left statue region");
if (autoRecoverTimer != null) {
autoRecoverTimer.cancel();
autoRecoverTimer = null;
}
}
// autoRevive automatically revives all team members.
public void autoRevive() {
player.getTeamManager().getActiveTeam().forEach(entity -> {
boolean isAlive = entity.isAlive();
if (isAlive) {
return;
}
logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName());
player.getTeamManager().reviveAvatar(entity.getAvatar());
player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0);
});
}
private class AutoRecoverTimerTick extends TimerTask {
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public void run() {
refillSpringVolume();
logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage());
if (getIsAutoRecoveryEnabled()) {
List<EntityAvatar> activeTeam = player.getTeamManager().getActiveTeam();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1;
if (priorityIndex >= 0) {
checkAndHealAvatar(activeTeam.get(priorityIndex));
}
for (int i = 0; i < activeTeam.size(); i++) {
if (i != priorityIndex) {
checkAndHealAvatar(activeTeam.get(i));
}
}
}
}
}
public void checkAndHealAvatar(EntityAvatar entity) {
int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100);
int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100);
if (currentHP == maxHP) {
return;
}
int targetHP = maxHP * getAutoRecoveryPercentage() / 100;
if (targetHP > currentHP) {
int needHP = targetHP - currentHP;
int currentVolume = getCurrentVolume();
if (currentVolume >= needHP) {
// sufficient
setCurrentVolume(currentVolume - needHP);
} else {
// insufficient balance
needHP = currentVolume;
setCurrentVolume(0);
}
if (needHP > 0) {
logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP);
player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP);
player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP,
((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER,
ChangeHpReason.CHANGE_HP_REASON_ADD_STATUE));
player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
}
}
}
public void refillSpringVolume() {
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
setMaxVolume(8500000);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage(100);
setIsAutoRecoveryEnabled(true);
int maxVolume = getMaxVolume();
int currentVolume = getCurrentVolume();
if (currentVolume < maxVolume) {
long now = System.currentTimeMillis() / 1000;
int secondsSinceLastUsed = (int) (now - getLastUsed());
// 15s = 1% max volume
int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100;
logger.trace("Statue has refilled HP volume: " + volumeRefilled);
currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume);
logger.trace("Statue remaining HP volume: " + currentVolume);
setCurrentVolume(currentVolume);
}
}
}

View File

@@ -1,94 +1,217 @@
package emu.grasscutter.game.managers.chat;
import emu.grasscutter.GameConstants;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.packet.BasePacket;
import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.packet.send.PacketPlayerChatNotify;
import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
import emu.grasscutter.server.packet.send.PacketPullPrivateChatRsp;
import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp;
import emu.grasscutter.utils.Utils;
import java.util.regex.Pattern;
import static emu.grasscutter.config.Configuration.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ChatManager implements ChatManagerHandler {
static final String PREFIXES = "[/!]";
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
static final String PREFIXES = "[/!]";
static final Pattern RE_PREFIXES = Pattern.compile(PREFIXES);
static final Pattern RE_COMMANDS = Pattern.compile("\n" + PREFIXES);
private final GameServer server;
// We store the chat history for ongoing sessions in the form
// user id -> chat partner id -> [messages]
private final Map<Integer, Map<Integer, List<ChatInfo>>> history = new HashMap<>();
public ChatManager(GameServer server) {
this.server = server;
}
private final GameServer server;
public GameServer getServer() {
return server;
}
public ChatManager(GameServer server) {
this.server = server;
}
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches())
return false;
for (String line : rawMessage.substring(1).split("\n[/!]"))
CommandMap.getInstance().invoke(sender, target, line);
return true;
}
public GameServer getServer() {
return server;
}
public void sendPrivateMessage(Player player, int targetUid, String message) {
// Sanity checks
if (message == null || message.length() == 0) {
return;
}
private boolean tryInvokeCommand(Player sender, Player target, String rawMessage) {
if (!RE_PREFIXES.matcher(rawMessage.substring(0, 1)).matches())
return false;
for (String line : rawMessage.substring(1).split("\n[/!]"))
CommandMap.getInstance().invoke(sender, target, line);
return true;
}
// Get target
Player target = getServer().getPlayerByUid(targetUid);
/********************
* Chat history handling
********************/
private void putInHistory(int uid, int targetId, ChatInfo info) {
if (!this.history.containsKey(uid)) {
this.history.put(uid, new HashMap<>());
}
if (!this.history.get(uid).containsKey(targetId)) {
this.history.get(uid).put(targetId, new ArrayList<>());
}
// Check if command
if (tryInvokeCommand(player, target, message)) {
return;
}
this.history.get(uid).get(targetId).add(info);
}
if (target == null) {
return;
}
public void clearHistoryOnLogout(Player player) {
if (this.history.containsKey(player.getUid())) {
this.history.remove(player.getUid());
}
}
// Create chat packet
BasePacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), message);
public void handlePullPrivateChatReq(Player player, int targetUid) {
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(targetUid)) {
player.sendPacket(new PacketPullPrivateChatRsp(this.history.get(player.getUid()).get(targetUid)));
}
else {
player.sendPacket(new PacketPullPrivateChatRsp(List.of()));
}
}
player.sendPacket(packet);
target.sendPacket(packet);
}
public void handlePullRecentChatReq(Player player) {
// For now, we send the list three messages from the server for the recent chat history.
// This matches the previous behavior, but ultimately, we should probably keep track of the last chat partner
// for every given player and return the last messages exchanged with that partner.
if (this.history.containsKey(player.getUid()) && this.history.get(player.getUid()).containsKey(GameConstants.SERVER_CONSOLE_UID)) {
int historyLength = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).size();
var messages = this.history.get(player.getUid()).get(GameConstants.SERVER_CONSOLE_UID).subList(Math.max(historyLength - 3, 0), historyLength);
player.sendPacket(new PacketPullRecentChatRsp(messages));
}
else {
player.sendPacket(new PacketPullRecentChatRsp(List.of()));
}
}
public void sendPrivateMessage(Player player, int targetUid, int emote) {
// Get target
Player target = getServer().getPlayerByUid(targetUid);
/********************
* Sending messages
********************/
public void sendPrivateMessageFromServer(int targetUid, String message) {
// Sanity checks.
if (message == null || message.length() == 0) {
return;
}
if (target == null) {
return;
}
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
// Create chat packet
BasePacket packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
// Create chat packet and put in history.
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, message);
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
player.sendPacket(packet);
target.sendPacket(packet);
}
// Send.
target.sendPacket(packet);
}
public void sendPrivateMessageFromServer(int targetUid, int emote) {
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
if (target == null) {
return;
}
public void sendTeamMessage(Player player, int channel, String message) {
// Sanity checks
if (message == null || message.length() == 0) {
return;
}
// Create chat packet and put in history.
var packet = new PacketPrivateChatNotify(GameConstants.SERVER_CONSOLE_UID, targetUid, emote);
putInHistory(targetUid, GameConstants.SERVER_CONSOLE_UID, packet.getChatInfo());
// Check if command
if (tryInvokeCommand(player, null, message)) {
return;
}
// Send.
target.sendPacket(packet);
}
// Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
}
public void sendPrivateMessage(Player player, int targetUid, String message) {
// Sanity checks.
if (message == null || message.length() == 0) {
return;
}
public void sendTeamMessage(Player player, int channel, int icon) {
// Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
}
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
// Check if command
if (tryInvokeCommand(player, target, message)) {
return;
}
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return;
}
// Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), targetUid, message);
// Send and put in history.
player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
if (target != null) {
target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
}
}
public void sendPrivateMessage(Player player, int targetUid, int emote) {
// Get target.
Player target = getServer().getPlayerByUid(targetUid);
if (target == null && targetUid != GameConstants.SERVER_CONSOLE_UID) {
return;
}
// Create chat packet.
var packet = new PacketPrivateChatNotify(player.getUid(), target.getUid(), emote);
// Send and put is history.
player.sendPacket(packet);
putInHistory(player.getUid(), targetUid, packet.getChatInfo());
if (target != null) {
target.sendPacket(packet);
putInHistory(targetUid, player.getUid(), packet.getChatInfo());
}
}
public void sendTeamMessage(Player player, int channel, String message) {
// Sanity checks
if (message == null || message.length() == 0) {
return;
}
// Check if command
if (tryInvokeCommand(player, null, message)) {
return;
}
// Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, message));
}
public void sendTeamMessage(Player player, int channel, int icon) {
// Create and send chat packet
player.getWorld().broadcastPacket(new PacketPlayerChatNotify(player, channel, icon));
}
/********************
* Welcome messages
********************/
public void sendServerWelcomeMessages(Player player) {
var joinOptions = GAME_INFO.joinOptions;
if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) {
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]);
}
if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) {
this.sendPrivateMessageFromServer(player.getUid(), joinOptions.welcomeMessage);
}
this.sendPrivateMessageFromServer(player.getUid(), "THIS IS AN EXPERIMENTAL BUILD OF GRASSCUTTER FOR 2.7.50/2.8\nDON'T LEAK <3");
}
}

View File

@@ -9,4 +9,10 @@ public interface ChatManagerHandler {
void sendPrivateMessage(Player player, int targetUid, int emote);
void sendTeamMessage(Player player, int channel, String message);
void sendTeamMessage(Player player, int channel, int icon);
void sendPrivateMessageFromServer(int targetUid, String message);
void sendPrivateMessageFromServer(int targetUid, int emote);
void handlePullPrivateChatReq(Player player, int targetUid);
void clearHistoryOnLogout(Player player);
void sendServerWelcomeMessages(Player player);
void handlePullRecentChatReq(Player player);
}

View File

@@ -1,30 +0,0 @@
package emu.grasscutter.game.managers.collection;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.utils.Position;
public class CollectionData {
Gadget gadget;
MotionInfo motionInfo;
Prop[] fightPropList;
static class GatherGadget{
int itemId;
}
static class Gadget{
int gadgetId;
int authorityPeerId;
int configId;
int groupId;
boolean isEnableInteract;
GatherGadget gatherGadget;
}
static class MotionInfo{
Position pos;
Position rot;
}
static class Prop{
int propType;
float propValue;
}
}

View File

@@ -1,74 +0,0 @@
package emu.grasscutter.game.managers.collection;
import java.util.HashMap;
import java.util.List;
import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.player.Player;
public class CollectionManager {
private static final long SECOND = 1000; //1 Second
private static final long MINUTE = SECOND*60; //1 Minute
private static final long HOUR = MINUTE*60; //1 Hour
private static final long DAY = HOUR*24; //1 Day
private static final HashMap<Integer,Long> DEFINE_REFRESH_TIME = new HashMap<>();// <GadgetId,Waiting Millisecond>
private static final long DEFAULT_REFRESH_TIME = HOUR*6; // default 6 Hours
static {
DEFINE_REFRESH_TIME.put(70590027,3*DAY);//星银矿石 3 Days
DEFINE_REFRESH_TIME.put(70590036,3*DAY);//紫晶块 3 Days
DEFINE_REFRESH_TIME.put(70520003,3*DAY);//水晶 3 Days
DEFINE_REFRESH_TIME.put(70590013,2*DAY);//嘟嘟莲 2 Days
DEFINE_REFRESH_TIME.put(70540029,2*DAY);//清心 2 Days
DEFINE_REFRESH_TIME.put(70540028,2*DAY);//星螺 2 Days
DEFINE_REFRESH_TIME.put(70540027,2*DAY);//马尾 2 Days
DEFINE_REFRESH_TIME.put(70540026,2*DAY);//琉璃袋 2 Days
DEFINE_REFRESH_TIME.put(70540022,2*DAY);//落落莓 2 Days
DEFINE_REFRESH_TIME.put(70540020,2*DAY);//慕风蘑菇 2 Days
DEFINE_REFRESH_TIME.put(70540019,2*DAY);//风车菊 2 Days
DEFINE_REFRESH_TIME.put(70540018,2*DAY);//塞西莉亚花 2 Days
DEFINE_REFRESH_TIME.put(70540015,2*DAY);//霓裳花 2 Days
DEFINE_REFRESH_TIME.put(70540014,2*DAY);//莲蓬 2 Days
DEFINE_REFRESH_TIME.put(70540013,2*DAY);//钩钩果 2 Days
DEFINE_REFRESH_TIME.put(70540012,2*DAY);//琉璃百合 2 Days
DEFINE_REFRESH_TIME.put(70540008,2*DAY);//绝云椒椒 2 Days
DEFINE_REFRESH_TIME.put(70520018,2*DAY);//夜泊石 2 Days
DEFINE_REFRESH_TIME.put(70520002,2*DAY);//白铁矿 2 Days
DEFINE_REFRESH_TIME.put(70510012,2*DAY);//石珀 2 Days
DEFINE_REFRESH_TIME.put(70510009,2*DAY);//蒲公英 2 Days
DEFINE_REFRESH_TIME.put(70510007,2*DAY);//冰雾花 2 Days
DEFINE_REFRESH_TIME.put(70510006,2*DAY);//烈焰花 2 Days
DEFINE_REFRESH_TIME.put(70510005,2*DAY);//电气水晶 2 Days
DEFINE_REFRESH_TIME.put(70510004,2*DAY);//小灯草 2 Days
DEFINE_REFRESH_TIME.put(70540021,DAY);//日落果 1 Day
DEFINE_REFRESH_TIME.put(70540005,DAY);//松果 1 Day
DEFINE_REFRESH_TIME.put(70540003,DAY);//苹果 1 Day
DEFINE_REFRESH_TIME.put(70540001,DAY);//树莓 1 Day
DEFINE_REFRESH_TIME.put(70520019,DAY);//魔晶块 1 Days
DEFINE_REFRESH_TIME.put(70520008,DAY);//金鱼草 1 Days
DEFINE_REFRESH_TIME.put(70520007,DAY);//白萝卜 1 Days
DEFINE_REFRESH_TIME.put(70520006,DAY);//胡萝卜 1 Days
DEFINE_REFRESH_TIME.put(70520004,DAY);//蘑菇 1 Day
DEFINE_REFRESH_TIME.put(70520001,DAY);//铁矿 1 Day
DEFINE_REFRESH_TIME.put(70520009,12*HOUR);//薄荷 12 Hours
DEFINE_REFRESH_TIME.put(70520005,12*HOUR);//甜甜花 12 Hours
}
private final static HashMap<Integer, List<CollectionData>> CollectionResourcesData = new HashMap<>();
private final HashMap<CollectionData,EntityGadget> spawnedEntities = new HashMap<>();
private CollectionRecordStore collectionRecordStore;
Player player;
private static long getGadgetRefreshTime(int gadgetId){
return DEFINE_REFRESH_TIME.getOrDefault(gadgetId,DEFAULT_REFRESH_TIME);
}
public synchronized void setPlayer(Player player) {
this.player = player;
this.collectionRecordStore = player.getCollectionRecordStore();
}
}

View File

@@ -1,67 +0,0 @@
package emu.grasscutter.game.managers.collection;
import java.util.HashMap;
import java.util.Map;
import dev.morphia.annotations.Entity;
@Entity
public class CollectionRecordStore {
private Map<Integer, CollectionRecord> records;
private Map<Integer, CollectionRecord> getRecords() {
if (records == null) {
records = new HashMap<>();
}
return records;
}
public void addRecord(int configId, long expiredMillisecond){
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
records.put(configId, new CollectionRecord(configId, expiredMillisecond + System.currentTimeMillis()));
}
}
public boolean findRecord(int configId) {
Map<Integer, CollectionRecord> records;
synchronized (records = getRecords()) {
CollectionRecord record = records.get(configId);
if (record == null) {
return false;
}
boolean expired = record.getExpiredTime() < System.currentTimeMillis();
if (expired) {
records.remove(configId);
return false;
}
return true;
}
}
@Entity
public static class CollectionRecord {
private int configId;
private long expiredTime;
@Deprecated // Morphia
public CollectionRecord() {}
public CollectionRecord(int configId, long expiredTime) {
this.configId = configId;
this.expiredTime = expiredTime;
}
public int getConfigId() {
return configId;
}
public long getExpiredTime() {
return expiredTime;
}
}
}

View File

@@ -7,20 +7,21 @@ import dev.morphia.annotations.Transient;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.HitTreeNotifyOuterClass;
import emu.grasscutter.net.proto.VectorOuterClass;
import emu.grasscutter.utils.Position;
public class DeforestationManager {
public class DeforestationManager extends BasePlayerManager {
final static int RECORD_EXPIRED_SECONDS = 60*5; // 5 min
final static int RECORD_MAX_TIMES = 3; // max number of wood
final static int RECORD_MAX_TIMES_OTHER_HIT_TREE = 10; // if hit 10 times other trees, reset wood
@Transient private final Player player;
@Transient private final ArrayList<HitTreeRecord> currentRecord;
@Transient private final static HashMap<Integer, Integer> ColliderTypeToWoodItemID = new HashMap<>();
private final ArrayList<HitTreeRecord> currentRecord;
private final static HashMap<Integer, Integer> ColliderTypeToWoodItemID = new HashMap<>();
static {
/* define wood types which reflected to item id*/
ColliderTypeToWoodItemID.put(1,101301);
@@ -36,20 +37,21 @@ public class DeforestationManager {
ColliderTypeToWoodItemID.put(11,101311);
ColliderTypeToWoodItemID.put(12,101312);
}
public DeforestationManager(Player player){
this.player = player;
public DeforestationManager(Player player) {
super(player);
this.currentRecord = new ArrayList<>();
}
public void resetWood(){
public void resetWood() {
synchronized (currentRecord) {
currentRecord.clear();
}
}
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit){
public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit) {
synchronized (currentRecord) {
//Grasscutter.getLogger().info("onDeforestationInvoke! Wood records {}", currentRecord);
VectorOuterClass.Vector hitPosition = hit.getHitPostion();
int woodType = hit.getWoodType();
VectorOuterClass.Vector hitPosition = hit.getTreePos();
int woodType = hit.getTreeType();
if (ColliderTypeToWoodItemID.containsKey(woodType)) {// is a available wood type
Scene scene = player.getScene();
int itemId = ColliderTypeToWoodItemID.get(woodType);
@@ -57,14 +59,14 @@ public class DeforestationManager {
HitTreeRecord record = searchRecord(positionHash);
if (record == null) {
record = new HitTreeRecord(positionHash);
}else{
}else {
currentRecord.remove(record);// move it to last position
}
currentRecord.add(record);
if(currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE){
if (currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE) {
currentRecord.remove(0);
}
if(record.record()) {
if (record.record()) {
EntityItem entity = new EntityItem(scene,
null,
GameData.getItemDataMap().get(itemId),
@@ -80,7 +82,7 @@ public class DeforestationManager {
}
// unknown wood type
}
private HitTreeRecord searchRecord(int id){
private HitTreeRecord searchRecord(int id) {
for (HitTreeRecord record : currentRecord) {
if (record.getUnique() == id) {
return record;

View File

@@ -13,6 +13,7 @@ import emu.grasscutter.game.entity.EntityItem;
import emu.grasscutter.game.entity.EntityMonster;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ElementType;
import emu.grasscutter.game.props.FightProperty;
@@ -31,8 +32,6 @@ import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
@@ -41,13 +40,14 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import static java.util.Map.entry;
import com.google.gson.reflect.TypeToken;
import com.google.protobuf.InvalidProtocolBufferException;
public class EnergyManager {
private final Player player;
public class EnergyManager extends BasePlayerManager {
private final Map<EntityAvatar, Integer> avatarNormalProbabilities;
// energyUsage for each player
private Boolean energyUsage;
@@ -55,18 +55,14 @@ public class EnergyManager {
private final static Int2ObjectMap<List<SkillParticleGenerationInfo>> skillParticleGenerationData = new Int2ObjectOpenHashMap<>();
public EnergyManager(Player player) {
this.player = player;
super(player);
this.avatarNormalProbabilities = new HashMap<>();
this.energyUsage=GAME_OPTIONS.energyUsage;
}
public Player getPlayer() {
return this.player;
}
public static void initialize() {
// Read the data we need for monster energy drops.
try (Reader fileReader = new InputStreamReader(DataLoader.load("EnergyDrop.json"))) {
try (Reader fileReader = DataLoader.loadReader("EnergyDrop.json")) {
List<EnergyDropEntry> energyDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, EnergyDropEntry.class).getType());
for (EnergyDropEntry entry : energyDropList) {
@@ -452,4 +448,4 @@ public class EnergyManager {
}
}
}
}
}

View File

@@ -11,8 +11,10 @@ import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.excels.ForgeData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.ItemUseOp;
import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData;
import emu.grasscutter.net.proto.ForgeQueueManipulateReqOuterClass.ForgeQueueManipulateReq;
@@ -27,301 +29,300 @@ import emu.grasscutter.server.packet.send.PacketForgeQueueManipulateRsp;
import emu.grasscutter.server.packet.send.PacketForgeStartRsp;
import emu.grasscutter.utils.Utils;
public class ForgingManager {
private final Player player;
public class ForgingManager extends BasePlayerManager {
public ForgingManager(Player player) {
this.player = player;
}
public ForgingManager(Player player) {
super(player);
}
/**********
Blueprint unlocking.
**********/
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
// Make sure this is actually a forging blueprint.
if (!blueprintItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) {
return false;
}
/**********
Blueprint unlocking.
**********/
public synchronized boolean unlockForgingBlueprint(GameItem blueprintItem) {
// Make sure this is actually a forging blueprint.
if (blueprintItem.getItemData().getItemUse().get(0).getUseOp() != ItemUseOp.ITEM_USE_UNLOCK_FORGE) {
return false;
}
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam().get(0));
// Determine the forging item we should unlock.
int forgeId = Integer.parseInt(blueprintItem.getItemData().getItemUse().get(0).getUseParam()[0]);
// Remove the blueprint from the player's inventory.
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
// update when unlocking the blueprint.
player.getInventory().removeItem(blueprintItem, 1);
// Remove the blueprint from the player's inventory.
// We need to do this here, before sending ForgeFormulaDataNotify, or the the forging UI won't correctly
// update when unlocking the blueprint.
player.getInventory().removeItem(blueprintItem, 1);
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
this.player.getUnlockedForgingBlueprints().add(forgeId);
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
this.player.getUnlockedForgingBlueprints().add(forgeId);
this.player.sendPacket(new PacketForgeFormulaDataNotify(forgeId));
return true;
}
return true;
}
/**********
Communicate forging information to the client.
**********/
private synchronized int determineNumberOfQueues() {
int adventureRank = player.getLevel();
return
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
}
/**********
Communicate forging information to the client.
**********/
private synchronized int determineNumberOfQueues() {
int adventureRank = player.getLevel();
return
(adventureRank >= 15) ? 4
: (adventureRank >= 10) ? 3
: (adventureRank >= 5) ? 2
: 1;
}
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
Map<Integer, ForgeQueueData> res = new HashMap<>();
int currentTime = Utils.getCurrentSeconds();
private synchronized Map<Integer, ForgeQueueData> determineCurrentForgeQueueData() {
Map<Integer, ForgeQueueData> res = new HashMap<>();
int currentTime = Utils.getCurrentSeconds();
// Create queue information for all active forges.
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
// Create queue information for all active forges.
for (int i = 0; i < this.player.getActiveForges().size(); i++) {
ActiveForgeData activeForge = this.player.getActiveForges().get(i);
ForgeQueueData data = ForgeQueueData.newBuilder()
.setQueueId(i + 1)
.setForgeId(activeForge.getForgeId())
.setFinishCount(activeForge.getFinishedCount(currentTime))
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
.setAvatarId(activeForge.getAvatarId())
.build();
ForgeQueueData data = ForgeQueueData.newBuilder()
.setQueueId(i + 1)
.setForgeId(activeForge.getForgeId())
.setFinishCount(activeForge.getFinishedCount(currentTime))
.setUnfinishCount(activeForge.getUnfinishedCount(currentTime))
.setTotalFinishTimestamp(activeForge.getTotalFinishTimestamp())
.setNextFinishTimestamp(activeForge.getNextFinishTimestamp(currentTime))
.setAvatarId(activeForge.getAvatarId())
.build();
res.put(i + 1, data);
}
res.put(i + 1, data);
}
return res;
}
return res;
}
public synchronized void sendForgeDataNotify() {
// Determine the number of queues and unlocked items.
int numQueues = this.determineNumberOfQueues();
var unlockedItems = this.player.getUnlockedForgingBlueprints();
var queueData = this.determineCurrentForgeQueueData();
public synchronized void sendForgeDataNotify() {
// Determine the number of queues and unlocked items.
int numQueues = this.determineNumberOfQueues();
var unlockedItems = this.player.getUnlockedForgingBlueprints();
var queueData = this.determineCurrentForgeQueueData();
// Send notification.
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
}
public synchronized void handleForgeGetQueueDataReq() {
// Determine the number of queues.
int numQueues = this.determineNumberOfQueues();
var queueData = this.determineCurrentForgeQueueData();
// Send notification.
this.player.sendPacket(new PacketForgeDataNotify(unlockedItems, numQueues, queueData));
}
// Reply.
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
}
public synchronized void handleForgeGetQueueDataReq() {
// Determine the number of queues.
int numQueues = this.determineNumberOfQueues();
var queueData = this.determineCurrentForgeQueueData();
/**********
Initiate forging process.
**********/
private synchronized void sendForgeQueueDataNotify() {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
var queueData = this.determineCurrentForgeQueueData();
// Reply.
this.player.sendPacket(new PacketForgeGetQueueDataRsp(Retcode.RET_SUCC, numQueues, queueData));
}
if (hasRemoved) {
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
}
/**********
Initiate forging process.
**********/
private synchronized void sendForgeQueueDataNotify() {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
private synchronized void sendForgeQueueDataNotify(boolean hasRemoved) {
var queueData = this.determineCurrentForgeQueueData();
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
if (hasRemoved) {
this.player.sendPacket(new PacketForgeQueueDataNotify(Map.of(), List.of(1, 2, 3, 4)));
}
public synchronized void handleForgeStartReq(ForgeStartReq req) {
// Refuse if all queues are already full.
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
return;
}
this.player.sendPacket(new PacketForgeQueueDataNotify(queueData, List.of()));
}
// Get the required forging information for the target item.
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
return;
}
public synchronized void handleForgeStartReq(ForgeStartReq req) {
// Refuse if all queues are already full.
if (this.player.getActiveForges().size() >= this.determineNumberOfQueues()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_QUEUE_FULL));
return;
}
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
// Get the required forging information for the target item.
if (!GameData.getForgeDataMap().containsKey(req.getForgeId())) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
return;
}
//Check if the player has sufficient forge points.
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
if (requiredPoints > this.player.getForgePoints()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
return;
}
// Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
material.add(new ItemParamData(202, forgeData.getScoinCost()));
ForgeData forgeData = GameData.getForgeDataMap().get(req.getForgeId());
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
//Check if the player has sufficient forge points.
int requiredPoints = forgeData.getForgePoint() * req.getForgeCount();
if (requiredPoints > this.player.getForgePoints()) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH));
return;
}
if (!success) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
}
// Check if we have enough of each material and consume.
List<ItemParamData> material = new ArrayList<>(forgeData.getMaterialItems());
material.add(new ItemParamData(202, forgeData.getScoinCost()));
// Consume forge points.
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
boolean success = player.getInventory().payItems(material.toArray(new ItemParamData[0]), req.getForgeCount(), ActionReason.ForgeCost);
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
activeForge.setAvatarId(req.getAvatarId());
activeForge.setCount(req.getForgeCount());
activeForge.setStartTime(Utils.getCurrentSeconds());
activeForge.setForgeTime(forgeData.getForgeTime());
if (!success) {
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_FORGE_POINT_NOT_ENOUGH)); //ToDo: Probably the wrong return code.
}
this.player.getActiveForges().add(activeForge);
// Consume forge points.
this.player.setForgePoints(this.player.getForgePoints() - requiredPoints);
// Done.
this.sendForgeQueueDataNotify();
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
}
// Create and add active forge.
ActiveForgeData activeForge = new ActiveForgeData();
activeForge.setForgeId(req.getForgeId());
activeForge.setAvatarId(req.getAvatarId());
activeForge.setCount(req.getForgeCount());
activeForge.setStartTime(Utils.getCurrentSeconds());
activeForge.setForgeTime(forgeData.getForgeTime());
/**********
Forge queue manipulation (obtaining results and cancelling forges).
**********/
private synchronized void obtainItems(int queueId) {
// Determin how many items are finished.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
this.player.getActiveForges().add(activeForge);
int finished = forge.getFinishedCount(currentTime);
int unfinished = forge.getUnfinishedCount(currentTime);
// Done.
this.sendForgeQueueDataNotify();
this.player.sendPacket(new PacketForgeStartRsp(Retcode.RET_SUCC));
}
// Sanity check: Are any items finished?
if (finished <= 0) {
return;
}
/**********
Forge queue manipulation (obtaining results and cancelling forges).
**********/
private synchronized void obtainItems(int queueId) {
// Determin how many items are finished.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
// Give finished items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
int finished = forge.getFinishedCount(currentTime);
int unfinished = forge.getUnfinishedCount(currentTime);
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
// Battle pass trigger handler
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
// Sanity check: Are any items finished?
if (finished <= 0) {
return;
}
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
// Give finished items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
remainingForge.setForgeId(forge.getForgeId());
remainingForge.setAvatarId(forge.getAvatarId());
remainingForge.setCount(unfinished);
remainingForge.setForgeTime(forge.getForgeTime());
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
int resultId = data.getResultItemId() > 0 ? data.getResultItemId() : data.getShowItemId();
ItemData resultItemData = GameData.getItemDataMap().get(resultId);
GameItem addItem = new GameItem(resultItemData, data.getResultItemCount() * finished);
this.player.getInventory().addItem(addItem);
this.player.getActiveForges().set(queueId - 1, remainingForge);
this.sendForgeQueueDataNotify();
}
// Otherwise, completely remove it.
else {
this.player.getActiveForges().remove(queueId - 1);
// this.sendForgeQueueDataNotify(queueId);
this.sendForgeQueueDataNotify(true);
}
// Battle pass trigger handler
this.player.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_DO_FORGE, 0, finished);
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
}
// Replace active forge with a new one for the unfinished items, if there are any.
if (unfinished > 0) {
ActiveForgeData remainingForge = new ActiveForgeData();
private synchronized void cancelForge(int queueId) {
// Make sure there are no unfinished items.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
remainingForge.setForgeId(forge.getForgeId());
remainingForge.setAvatarId(forge.getAvatarId());
remainingForge.setCount(unfinished);
remainingForge.setForgeTime(forge.getForgeTime());
remainingForge.setStartTime(forge.getStartTime() + finished * forge.getForgeTime());
if (forge.getFinishedCount(currentTime) > 0) {
return;
}
// Return material items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
this.player.getActiveForges().set(queueId - 1, remainingForge);
this.sendForgeQueueDataNotify();
}
// Otherwise, completely remove it.
else {
this.player.getActiveForges().remove(queueId - 1);
// this.sendForgeQueueDataNotify(queueId);
this.sendForgeQueueDataNotify(true);
}
var returnItems = new ArrayList<GameItem>();
for (var material : data.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT, List.of(addItem), List.of(), List.of()));
}
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
private synchronized void cancelForge(int queueId) {
// Make sure there are no unfinished items.
int currentTime = Utils.getCurrentSeconds();
ActiveForgeData forge = this.player.getActiveForges().get(queueId - 1);
this.player.getInventory().addItem(returnItem);
returnItems.add(returnItem);
}
if (forge.getFinishedCount(currentTime) > 0) {
return;
}
// Return Mora to the player.
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
// Return material items to the player.
ForgeData data = GameData.getForgeDataMap().get(forge.getForgeId());
ItemData moraItem = GameData.getItemDataMap().get(202);
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
var returnItems = new ArrayList<GameItem>();
for (var material : data.getMaterialItems()) {
if (material.getItemId() == 0) {
continue;
}
// Return forge points to the player.
int requiredPoints = data.getForgePoint() * forge.getCount();
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
ItemData resultItemData = GameData.getItemDataMap().get(material.getItemId());
GameItem returnItem = new GameItem(resultItemData, material.getItemCount() * forge.getCount());
this.player.setForgePoints(newPoints);
this.player.getInventory().addItem(returnItem);
returnItems.add(returnItem);
}
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
// Return Mora to the player.
this.player.setMora(this.player.getMora() + data.getScoinCost() * forge.getCount());
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
}
ItemData moraItem = GameData.getItemDataMap().get(202);
GameItem returnMora = new GameItem(moraItem, data.getScoinCost() * forge.getCount());
returnItems.add(returnMora);
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
// Get info from the request.
int queueId = req.getForgeQueueId();
var manipulateType = req.getManipulateType();
// Handle according to the manipulation type.
switch (manipulateType) {
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
this.obtainItems(queueId);
break;
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
this.cancelForge(queueId);
break;
default:
break; //Should never happen.
}
}
// Return forge points to the player.
int requiredPoints = data.getForgePoint() * forge.getCount();
int newPoints = Math.min(this.player.getForgePoints() + requiredPoints, 300_000);
/**********
Periodic forging updates.
**********/
public synchronized void sendPlayerForgingUpdate() {
int currentTime = Utils.getCurrentSeconds();
this.player.setForgePoints(newPoints);
// Determine if sending an update is necessary.
// We only send an update if there are forges in the forge queue
// that have changed since the last notification.
if (this.player.getActiveForges().size() <= 0) {
return;
}
// Remove the forge queue.
this.player.getActiveForges().remove(queueId - 1);
this.sendForgeQueueDataNotify(true);
boolean hasChanges = this.player.getActiveForges().stream()
.filter(forge -> forge.updateChanged(currentTime))
.findAny()
.isPresent();
// Send response.
this.player.sendPacket(new PacketForgeQueueManipulateRsp(Retcode.RET_SUCC, ForgeQueueManipulateType.FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE, List.of(), returnItems, List.of()));
}
if (!hasChanges) {
return;
}
public synchronized void handleForgeQueueManipulateReq(ForgeQueueManipulateReq req) {
// Get info from the request.
int queueId = req.getForgeQueueId();
var manipulateType = req.getManipulateType();
// Send notification.
this.sendForgeQueueDataNotify();
// Handle according to the manipulation type.
switch (manipulateType) {
case FORGE_QUEUE_MANIPULATE_TYPE_RECEIVE_OUTPUT:
this.obtainItems(queueId);
break;
case FORGE_QUEUE_MANIPULATE_TYPE_STOP_FORGE:
this.cancelForge(queueId);
break;
default:
break; //Should never happen.
}
}
// Reset changed flags.
this.player.getActiveForges().stream()
.forEach(forge -> forge.setChanged(false));
}
/**********
Periodic forging updates.
**********/
public synchronized void sendPlayerForgingUpdate() {
int currentTime = Utils.getCurrentSeconds();
// Determine if sending an update is necessary.
// We only send an update if there are forges in the forge queue
// that have changed since the last notification.
if (this.player.getActiveForges().size() <= 0) {
return;
}
boolean hasChanges = this.player.getActiveForges().stream()
.filter(forge -> forge.updateChanged(currentTime))
.findAny()
.isPresent();
if (!hasChanges) {
return;
}
// Send notification.
this.sendForgeQueueDataNotify();
// Reset changed flags.
this.player.getActiveForges().stream()
.forEach(forge -> forge.setChanged(false));
}
}

View File

@@ -1,5 +1,6 @@
package emu.grasscutter.game.managers.mapmark;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType;
import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq;
@@ -10,16 +11,17 @@ import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
import emu.grasscutter.utils.Position;
import java.util.HashMap;
import java.util.Map;
public class MapMarksManager {
public class MapMarksManager extends BasePlayerManager {
public static final int mapMarkMaxCount = 150;
private HashMap<String, MapMark> mapMarks;
private final Player player;
public MapMarksManager(Player player) {
this.player = player;
this.mapMarks = player.getMapMarks();
if (this.mapMarks == null) { this.mapMarks = new HashMap<>(); }
super(player);
}
public Map<String, MapMark> getMapMarks() {
return getPlayer().getMapMarks();
}
public void handleMapMarkReq(MarkMapReq req) {
@@ -46,34 +48,25 @@ public class MapMarksManager {
}
}
if (op != Operation.OPERATION_GET) {
saveMapMarks();
save();
}
player.getSession().send(new PacketMarkMapRsp(getMapMarks()));
}
public HashMap<String, MapMark> getMapMarks() {
return mapMarks;
}
public String getMapMarkKey(Position position) {
return "x" + (int)position.getX()+ "z" + (int)position.getZ();
}
public void removeMapMark(Position position) {
mapMarks.remove(getMapMarkKey(position));
getMapMarks().remove(getMapMarkKey(position));
}
public void addMapMark(MapMark mapMark) {
if (mapMarks.size() < mapMarkMaxCount) {
mapMarks.put(getMapMarkKey(mapMark.getPosition()), mapMark);
if (getMapMarks().size() < mapMarkMaxCount) {
getMapMarks().put(getMapMarkKey(mapMark.getPosition()), mapMark);
}
}
private void saveMapMarks() {
player.setMapMarks(mapMarks);
player.save();
}
private void teleport(Player player, MapMark mapMark) {
float y; try {
y = (float)Integer.parseInt(mapMark.getName());
@@ -83,13 +76,13 @@ public class MapMarksManager {
Position pos = mapMark.getPosition();
PlayerTeleportEvent event = new PlayerTeleportEvent(player, PlayerTeleportEvent.TeleportType.MAP,
player.getPos(), new Position(pos.getX(), y, pos.getZ()));
player.getPosition(), new Position(pos.getX(), y, pos.getZ()));
event.call(); if(event.isCanceled()) return;
player.getPos().set(event.getDestination());
player.getPosition().set(event.getDestination());
if (mapMark.getSceneId() != player.getSceneId()) {
player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), player.getPos());
player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), player.getPosition());
} player.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(player));
}
}

View File

@@ -5,6 +5,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.entity.GameEntity;
import emu.grasscutter.game.player.BasePlayerManager;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.LifeState;
@@ -21,14 +22,13 @@ import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
import org.jetbrains.annotations.NotNull;
import static emu.grasscutter.config.Configuration.GAME_OPTIONS;
import java.util.*;
import static emu.grasscutter.Configuration.GAME_OPTIONS;
public class StaminaManager {
public class StaminaManager extends BasePlayerManager {
// TODO: Skiff state detection?
private final Player player;
private static final HashMap<String, HashSet<MotionState>> MotionStatesCategorized = new HashMap<>() {{
put("CLIMB", new HashSet<>(List.of(
MotionState.MOTION_STATE_CLIMB, // sustained, when not moving no cost no recover
@@ -159,11 +159,11 @@ public class StaminaManager {
}};
public static void initialize() {
// TODO: Initialize foods etc.
// TODO: Initialize foods etc.
}
public StaminaManager(Player player) {
this.player = player;
super(player);
}
// Accessors
@@ -529,20 +529,20 @@ public class StaminaManager {
}
// Bow avatar charged attack
Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar();
switch (currentAvatar.getAvatarData().getWeaponType()) {
case WEAPON_BOW:
return getBowSustainedCost(skillCasting);
case WEAPON_CLAYMORE:
return getClaymoreSustainedCost(skillCasting);
case WEAPON_CATALYST:
return getCatalystCost(skillCasting);
case WEAPON_POLE:
return getPolearmCost(skillCasting);
case WEAPON_SWORD_ONE_HAND:
return getSwordCost(skillCasting);
case WEAPON_BOW:
return getBowSustainedCost(skillCasting);
case WEAPON_CLAYMORE:
return getClaymoreSustainedCost(skillCasting);
case WEAPON_CATALYST:
return getCatalystCost(skillCasting);
case WEAPON_POLE:
return getPolearmCost(skillCasting);
case WEAPON_SWORD_ONE_HAND:
return getSwordCost(skillCasting);
}
return new Consumption();
}