server-master/srcs/_plugins/WingsEmu.Plugins.PacketHandling/Game/Npc/PdtsePacketHandler.cs
2026-02-10 18:21:30 +01:00

325 lines
No EOL
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WingsAPI.Game.Extensions.ItemExtension.Inventory;
using WingsAPI.Game.Extensions.ItemExtension.Item;
using WingsAPI.Game.Extensions.Quests;
using WingsEmu.DTOs.Maps;
using WingsEmu.DTOs.Quests;
using WingsEmu.DTOs.Recipes;
using WingsEmu.Game;
using WingsEmu.Game._enum;
using WingsEmu.Game._i18n;
using WingsEmu.Game.Configurations;
using WingsEmu.Game.Entities;
using WingsEmu.Game.Entities.Event;
using WingsEmu.Game.Extensions;
using WingsEmu.Game.Inventory;
using WingsEmu.Game.Items;
using WingsEmu.Game.Managers;
using WingsEmu.Game.Managers.ServerData;
using WingsEmu.Game.Managers.StaticData;
using WingsEmu.Game.Networking;
using WingsEmu.Game.Npcs.Event;
using WingsEmu.Game.Portals;
using WingsEmu.Game.Quests;
using WingsEmu.Game.Quests.Event;
using WingsEmu.Packets.ClientPackets;
using WingsEmu.Packets.Enums;
using WingsEmu.Packets.Enums.Chat;
namespace WingsEmu.Plugins.PacketHandling.Game.Npc;
public class PdtsePacketHandler : GenericGamePacketHandlerBase<PdtsePacket>
{
private readonly IGameItemInstanceFactory _gameItemInstanceFactory;
private readonly IItemsManager _itemsManager;
private readonly IItemUsageManager _itemUsageManager;
private readonly IGameLanguageService _language;
private readonly INpcEntityFactory _npcEntityFactory;
private readonly INpcMonsterManager _npcMonsterManager;
private readonly IRandomGenerator _randomGenerator;
private readonly IRecipeManager _recipeManager;
private readonly ITimeSpaceConfiguration _timeSpaceConfiguration;
public PdtsePacketHandler(IGameLanguageService language, IRecipeManager recipeManager, IItemUsageManager itemUsageManager,
IGameItemInstanceFactory gameItemInstanceFactory, IItemsManager itemsManager, IRandomGenerator randomGenerator,
ITimeSpaceConfiguration timeSpaceConfiguration, INpcEntityFactory npcEntityFactory,
INpcMonsterManager npcMonsterManager)
{
_language = language;
_recipeManager = recipeManager;
_itemUsageManager = itemUsageManager;
_gameItemInstanceFactory = gameItemInstanceFactory;
_itemsManager = itemsManager;
_randomGenerator = randomGenerator;
_timeSpaceConfiguration = timeSpaceConfiguration;
_npcEntityFactory = npcEntityFactory;
_npcMonsterManager = npcMonsterManager;
}
protected override async Task HandlePacketAsync(IClientSession session, PdtsePacket packet)
{
if (!session.HasCurrentMapInstance)
{
return;
}
short vNum = packet.VNum;
int lastItem = session.PlayerEntity.LastMinilandProducedItem ?? _itemUsageManager.GetLastItemUsed(session.PlayerEntity.Id);
int lastNpcId = session.PlayerEntity.LastNRunId;
short? eqItemSlot = packet.EqItemSlot;
IReadOnlyList<Recipe> producerItemRecipes = _recipeManager.GetRecipesByProducerItemVnum(lastItem);
Recipe recipe = producerItemRecipes?.FirstOrDefault(x => x.ProducedItemVnum == vNum);
bool isNpcRecipe = false;
if (recipe == null)
{
IReadOnlyList<Recipe> npcRecipes = _recipeManager.GetRecipesByNpcId(lastNpcId) ??
_recipeManager.GetRecipesByNpcMonsterVnum(session.CurrentMapInstance.GetNpcById(lastNpcId)?.MonsterVNum ?? 0);
recipe = npcRecipes?.FirstOrDefault(x => x.ProducedItemVnum == vNum);
isNpcRecipe = true;
}
if (recipe is not { Amount: > 0 })
{
session.SendEmptyRecipeCraftItem();
session.PlayerEntity.IsCraftingItem = false;
return;
}
if (packet.Type == 1)
{
session.SendRecipeCraftItemList(recipe);
session.PlayerEntity.IsCraftingItem = true;
return;
}
if (!recipe.Items.Any() || recipe.Items.Any(ite => !session.PlayerEntity.HasItem(ite.ItemVNum, ite.Amount)))
{
session.PlayerEntity.IsCraftingItem = false;
return;
}
IGameItem producedItem = _itemsManager.GetItem(recipe.ProducedItemVnum);
if (producedItem == null)
{
return;
}
if (!session.PlayerEntity.HasSpaceFor(recipe.ProducedItemVnum, (short)recipe.Amount) && !producedItem.IsTimeSpaceStone())
{
session.SendChatMessage(_language.GetLanguage(GameDialogKey.INTERACTION_MESSAGE_NOT_ENOUGH_PLACE, session.UserLanguage), ChatMessageColorType.Yellow);
session.PlayerEntity.IsCraftingItem = false;
return;
}
bool close = false;
short rarity = 0;
short upgrade = 0;
if (!CanCraftTimeSpace(session, producedItem, _timeSpaceConfiguration))
{
session.SendShopEndPacket(ShopEndType.Player);
return;
}
foreach (RecipeItemDTO recipeItem in recipe.Items)
{
InventoryItem getFirstItem = session.PlayerEntity.GetFirstItemByVnum(recipeItem.ItemVNum);
IGameItem eqItem = _itemsManager.GetItem(recipeItem.ItemVNum);
bool isEquipment = false;
if (eqItem.Type == InventoryType.Equipment && recipeItem.Slot == 0)
{
close = true;
isEquipment = true;
getFirstItem = eqItemSlot.HasValue ? session.PlayerEntity.GetItemBySlotAndType(eqItemSlot.Value, InventoryType.Equipment) : null;
getFirstItem ??= session.PlayerEntity.GetFirstItemByVnum(recipeItem.ItemVNum);
}
if (!isEquipment)
{
await session.RemoveItemFromInventory(recipeItem.ItemVNum, recipeItem.Amount);
}
else
{
await session.RemoveItemFromInventory(amount: recipeItem.Amount, item: getFirstItem);
}
if (session.PlayerEntity.HasItem(recipeItem.ItemVNum, recipeItem.Amount))
{
continue;
}
close = true;
}
GameItemInstance newItem = _gameItemInstanceFactory.CreateItem(recipe.ProducedItemVnum, recipe.Amount, (byte)upgrade, (sbyte)rarity);
if (newItem.GameItem.IsTimeSpaceStone())
{
await CreateTimeSpaceNpc(session, newItem.GameItem);
return;
}
await ProcessCraftingQuest(session, newItem);
if (!isNpcRecipe && close || !session.PlayerEntity.IsCraftingItem)
{
session.SendShopEndPacket(ShopEndType.Player);
}
await session.EmitEventAsync(new ItemProducedEvent
{
ItemInstance = newItem,
ItemAmount = recipe.Amount
});
session.SendSound(SoundType.CRAFTING_SUCCESS);
string itemName = _language.GetLanguage(GameDataType.Item, newItem.GameItem.Name, session.UserLanguage);
session.SendMsg(_language.GetLanguageFormat(GameDialogKey.ITEM_SHOUTMESSAGE_CRAFTED_OBJECT, session.UserLanguage, itemName, recipe.Amount.ToString()), MsgMessageType.Middle);
_itemUsageManager.SetLastItemUsed(session.PlayerEntity.Id, 0);
}
private bool CanCraftTimeSpace(IClientSession session, IGameItem item, ITimeSpaceConfiguration timeSpaceConfiguration)
{
// If it's not TS crafted stone
if (!item.IsTimeSpaceStone())
{
return true;
}
if (!session.CurrentMapInstance.HasMapFlag(MapFlags.IS_BASE_MAP))
{
session.SendMsg(session.GetLanguage(GameDialogKey.INFORMATION_SHOUTMESSAGE_MUST_BE_IN_CLASSIC_MAP), MsgMessageType.Middle);
return false;
}
int mapVnum = item.Data[3];
if (mapVnum == -1 && session.CurrentMapInstance.HasMapFlag(MapFlags.ACT_4))
{
session.SendMsg(session.GetLanguage(GameDialogKey.INFORMATION_SHOUTMESSAGE_MUST_BE_IN_CLASSIC_MAP), MsgMessageType.Middle);
return false;
}
if (mapVnum != -1 && session.CurrentMapInstance.MapVnum != mapVnum)
{
session.SendMsg(session.GetLanguage(GameDialogKey.TIMESPACE_SHOUTMESSAGE_WRONG_MAP), MsgMessageType.Middle);
return false;
}
// Can't create portal close to another one
IPortalEntity portal = session.CurrentMapInstance.Portals.Concat(session.PlayerEntity.GetExtraPortal()).FirstOrDefault(s =>
session.PlayerEntity.PositionY >= s.PositionY - 3 &&
session.PlayerEntity.PositionY <= s.PositionY + 3 &&
session.PlayerEntity.PositionX >= s.PositionX - 3 &&
session.PlayerEntity.PositionX <= s.PositionX + 3);
ITimeSpacePortalEntity tsPortal = session.CurrentMapInstance.TimeSpacePortals.FirstOrDefault(s =>
session.PlayerEntity.PositionY >= s.Position.Y - 3 &&
session.PlayerEntity.PositionY <= s.Position.Y + 3 &&
session.PlayerEntity.PositionX >= s.Position.X - 3 &&
session.PlayerEntity.PositionX <= s.Position.X + 3);
INpcEntity anotherTimeSpaceNpc = session.CurrentMapInstance.GetPassiveNpcs().FirstOrDefault(s =>
session.PlayerEntity.PositionY >= s.Position.Y - 3 &&
session.PlayerEntity.PositionY <= s.Position.Y + 3 &&
session.PlayerEntity.PositionX >= s.Position.X - 3 &&
session.PlayerEntity.PositionX <= s.Position.X + 3 && s.TimeSpaceOwnerId.HasValue);
if (portal != null || tsPortal != null || anotherTimeSpaceNpc != null)
{
session.SendMsg(session.GetLanguage(GameDialogKey.TIMESPACE_SHOUTMESSAGE_CLOSE_PORTAL), MsgMessageType.Middle);
return false;
}
// Check if player created already Time-Space npc
INpcEntity timeSpaceNpc = session.CurrentMapInstance.GetPassiveNpcs().FirstOrDefault(x => x.TimeSpaceOwnerId.HasValue && session.PlayerEntity.Id == x.TimeSpaceOwnerId.Value);
if (timeSpaceNpc != null)
{
return false;
}
TimeSpaceFileConfiguration timeSpace = _timeSpaceConfiguration.GetTimeSpaceConfiguration(item.Data[2]);
return timeSpace != null;
}
private async Task CreateTimeSpaceNpc(IClientSession session, IGameItem item)
{
session.SendSound(SoundType.CRAFTING_SUCCESS);
session.SendShopEndPacket(ShopEndType.Player);
_itemUsageManager.SetLastItemUsed(session.PlayerEntity.Id, 0);
TimeSpaceFileConfiguration timeSpace = _timeSpaceConfiguration.GetTimeSpaceConfiguration(item.Data[2]);
if (timeSpace == null)
{
return;
}
IMonsterData timeSpaceMonster = _npcMonsterManager.GetNpc((short)MonsterVnum.TIME_SPACE_NPC);
if (timeSpaceMonster == null)
{
return;
}
INpcEntity newNpc = _npcEntityFactory.CreateNpc(timeSpaceMonster, session.CurrentMapInstance, null, new NpcAdditionalData
{
TimeSpaceInfo = timeSpace,
TimeSpaceOwnerId = session.PlayerEntity.Id
});
await newNpc.EmitEventAsync(new MapJoinNpcEntityEvent(newNpc, session.PlayerEntity.Position.X, session.PlayerEntity.Position.Y));
string packet = newNpc.GenerateEffectGround(EffectType.BlueTimeSpace, newNpc.PositionX, newNpc.PositionY, false);
newNpc.MapInstance.Broadcast(x => packet);
}
private async Task ProcessCraftingQuest(IClientSession session, GameItemInstance itemCrafted)
{
IReadOnlyCollection<CharacterQuest> characterQuests = session.PlayerEntity.GetCurrentQuests()
.Where(s => s.Quest.QuestType == QuestType.CRAFT_WITHOUT_KEEPING && s.Quest.Objectives.Any(o => o.Data0 == itemCrafted.ItemVNum)).ToList();
if (!characterQuests.Any())
{
InventoryItem invItem = await session.AddNewItemToInventory(itemCrafted, sendGiftIsFull: true);
GameItemInstance item = invItem.ItemInstance;
if (item.GameItem.EquipmentSlot is EquipmentType.Armor or EquipmentType.MainWeapon or EquipmentType.SecondaryWeapon)
{
item.SetRarityPoint(_randomGenerator);
}
session.SendPdtiPacket(PdtiType.ItemHasBeenProduced, itemCrafted.ItemVNum, (short)itemCrafted.Amount, invItem.Slot, itemCrafted.Upgrade, itemCrafted.Rarity);
return;
}
foreach (CharacterQuest quest in characterQuests)
{
IEnumerable<QuestObjectiveDto> objectives = quest.Quest.Objectives;
foreach (QuestObjectiveDto objective in objectives)
{
if (objective.Data0 != itemCrafted.ItemVNum)
{
continue;
}
CharacterQuestObjectiveDto questObjectiveDto = quest.ObjectiveAmount[objective.ObjectiveIndex];
int objectiveAmount = Math.Min(itemCrafted.Amount, quest.ObjectiveAmount[objective.ObjectiveIndex].RequiredAmount);
questObjectiveDto.CurrentAmount += objectiveAmount;
await session.EmitEventAsync(new QuestObjectiveUpdatedEvent
{
CharacterQuest = quest
});
if (session.PlayerEntity.IsQuestCompleted(quest))
{
await session.EmitEventAsync(new QuestCompletedEvent(quest));
}
}
}
}
}