Added inventory controller implementation

This commit is contained in:
Chomp
2025-01-21 20:24:09 +00:00
parent d9643ef519
commit 3989fa5e58
10 changed files with 873 additions and 242 deletions
+51 -46
View File
@@ -1,9 +1,10 @@
using SptCommon.Annotations;
using Core.Controllers;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Inventory;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Quests;
using SptCommon.Annotations;
namespace Core.Callbacks;
@@ -14,77 +15,80 @@ public class InventoryCallbacks(
)
{
/// <summary>
/// Handle client/game/profile/items/moving Move event
/// Handle client/game/profile/items/moving Move event
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse MoveItem(PmcData pmcData, InventoryMoveRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse MoveItem(PmcData pmcData, InventoryMoveRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.MoveItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
/// Handle Remove event
/// Handle Remove event
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse RemoveItem(PmcData pmcData, InventoryRemoveRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse RemoveItem(PmcData pmcData, InventoryRemoveRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.RemoveItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
/// Handle Split event
/// Handle Split event
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse SplitItem(PmcData pmcData, InventorySplitRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse SplitItem(PmcData pmcData, InventorySplitRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.SplitItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse MergeItem(PmcData pmcData, InventoryMergeRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse MergeItem(PmcData pmcData, InventoryMergeRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.MergeItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse TransferItem(PmcData pmcData, InventoryTransferRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse TransferItem(PmcData pmcData, InventoryTransferRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.TransferItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
/// Handle Swap
/// Handle Swap
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
@@ -96,7 +100,6 @@ public class InventoryCallbacks(
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
@@ -108,7 +111,6 @@ public class InventoryCallbacks(
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
@@ -120,136 +122,138 @@ public class InventoryCallbacks(
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="request"></param>
/// <param name="sessionId"></param>
/// <returns></returns>
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData info, string sessionID)
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData request, string sessionId)
{
return _inventoryController.TagItem(pmcData, info, sessionID);
return _inventoryController.TagItem(pmcData, request, sessionId);
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse BindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse BindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.BindItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse UnBindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse UnBindItem(PmcData pmcData, InventoryBindRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.UnBindItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse ExamineItem(PmcData pmcData, InventoryExamineRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse ExamineItem(PmcData pmcData, InventoryExamineRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.ExamineItem(pmcData, info, sessionID, output);
return output;
}
/// <summary>
/// Handle ReadEncyclopedia
/// Handle ReadEncyclopedia
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <returns></returns>
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData info, string sessionID)
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData info,
string sessionID)
{
return _inventoryController.ReadEncyclopedia(pmcData, info, sessionID);
}
/// <summary>
/// Handle ApplyInventoryChanges
/// Handle ApplyInventoryChanges
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse SortInventory(PmcData pmcData, InventorySortRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse SortInventory(PmcData pmcData, InventorySortRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.SortInventory(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData info,
string sessionID, ItemEventRouterResponse output)
{
_inventoryController.CreateMapMarker(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData info,
string sessionID, ItemEventRouterResponse output)
{
_inventoryController.DeleteMapMarker(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.EditMapMarker(pmcData, info, sessionID, output);
return output;
}
/// <summary>
/// Handle OpenRandomLootContainer
/// Handle OpenRandomLootContainer
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData info, string sessionID,
public ItemEventRouterResponse OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData info,
string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.OpenRandomLootContainer(pmcData, info, sessionID, output);
@@ -257,57 +261,58 @@ public class InventoryCallbacks(
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.RedeemProfileReward(pmcData, info, sessionID);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse SetFavoriteItem(PmcData pmcData, SetFavoriteItems info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse SetFavoriteItem(PmcData pmcData, SetFavoriteItems info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.SetFavoriteItem(pmcData, info, sessionID);
return output;
}
/// <summary>
/// TODO: MOVE INTO QUEST CODE
/// Handle game/profile/items/moving - QuestFail
/// TODO: MOVE INTO QUEST CODE
/// Handle game/profile/items/moving - QuestFail
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse FailQuest(PmcData pmcData, FailQuestRequestData info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse FailQuest(PmcData pmcData, FailQuestRequestData info, string sessionID,
ItemEventRouterResponse output)
{
_questController.FailQuest(pmcData, info, sessionID, output);
return output;
}
/// <summary>
///
/// </summary>
/// <param name="pmcData"></param>
/// <param name="info"></param>
/// <param name="sessionID"></param>
/// <param name="output"></param>
/// <returns></returns>
public ItemEventRouterResponse PinOrLock(PmcData pmcData, PinOrLockItemRequest info, string sessionID, ItemEventRouterResponse output)
public ItemEventRouterResponse PinOrLock(PmcData pmcData, PinOrLockItemRequest info, string sessionID,
ItemEventRouterResponse output)
{
_inventoryController.PinOrLock(pmcData, info, sessionID, output);
return output;
+631 -54
View File
@@ -2,15 +2,18 @@ using SptCommon.Annotations;
using Core.Generators;
using Core.Helpers;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Inventory;
using Core.Models.Eft.ItemEvent;
using Core.Models.Eft.Quests;
using Core.Models.Enums;
using Core.Models.Utils;
using Core.Routers;
using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using Core.Models.Eft.Profile;
using Core.Models.Spt.Dialog;
using Product = Core.Models.Eft.ItemEvent.Product;
namespace Core.Controllers;
@@ -27,6 +30,7 @@ public class InventoryController(
ProfileHelper _profileHelper,
PaymentHelper _paymentHelper,
TraderHelper _traderHelper,
ItemHelper _itemHelper,
DatabaseService _databaseService,
FenceService _fenceService,
RagfairOfferService _ragfairOfferService,
@@ -38,15 +42,13 @@ public class InventoryController(
ICloner _cloner
)
{
public void MoveItem(PmcData pmcData, InventoryMoveRequestData moveRequest, string sessionID, ItemEventRouterResponse output)
public void MoveItem(PmcData pmcData, InventoryMoveRequestData moveRequest, string sessionId,
ItemEventRouterResponse output)
{
if (output.Warnings?.Count > 0)
{
return;
}
if (output.Warnings?.Count > 0) return;
// Changes made to result apply to character inventory
var ownerInventoryItems = _inventoryHelper.GetOwnerInventoryItems(moveRequest, moveRequest.Item, sessionID);
var ownerInventoryItems = _inventoryHelper.GetOwnerInventoryItems(moveRequest, moveRequest.Item, sessionId);
if (ownerInventoryItems.SameInventory.GetValueOrDefault(false))
{
// Don't move items from trader to profile, this can happen when editing a traders preset weapons
@@ -57,7 +59,7 @@ public class InventoryController(
}
// Check for item in inventory before allowing internal transfer
var originalItemLocation = ownerInventoryItems.From?.FirstOrDefault((item) => item.Id == moveRequest.Item);
var originalItemLocation = ownerInventoryItems.From?.FirstOrDefault(item => item.Id == moveRequest.Item);
if (originalItemLocation is null)
{
// Internal item move but item never existed, possible dupe glitch
@@ -67,22 +69,30 @@ public class InventoryController(
var originalLocationSlotId = originalItemLocation.SlotId;
var moveResult = _inventoryHelper.MoveItemInternal(pmcData, ownerInventoryItems.From ?? [], moveRequest, out var errorMessage);
var moveResult = _inventoryHelper.MoveItemInternal(
pmcData,
ownerInventoryItems.From ?? [],
moveRequest,
out var errorMessage
);
if (!moveResult)
{
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
// Item is moving into or out of place of fame dogtag slot
if (moveRequest.To?.Container != null && (moveRequest.To.Container.StartsWith("dogtag") || originalLocationSlotId!.StartsWith("dogtag")))
{
// Item is moving into or out of place of fame dog tag slot
if (moveRequest.To?.Container != null &&
(moveRequest.To.Container.StartsWith("dogtag") || originalLocationSlotId.StartsWith("dogtag")))
_hideoutHelper.ApplyPlaceOfFameDogtagBonus(pmcData);
}
}
else
{
_inventoryHelper.MoveItemToProfile(ownerInventoryItems.From ?? [], ownerInventoryItems.To ?? [], moveRequest);
_inventoryHelper.MoveItemToProfile(
ownerInventoryItems.From ?? [],
ownerInventoryItems.To ?? [],
moveRequest
);
}
}
@@ -95,103 +105,670 @@ public class InventoryController(
);
}
public void PinOrLock(PmcData pmcData, PinOrLockItemRequest info, string sessionId, ItemEventRouterResponse output)
public void PinOrLock(PmcData pmcData, PinOrLockItemRequest request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
var itemToAdjust = pmcData.Inventory!.Items!.FirstOrDefault(item => item.Id == request.Item);
if (itemToAdjust is null)
{
_logger.Error($"Unable find item: {request.Item} to: {request.State} on player {sessionId}to: ");
return;
}
// Nullguard
itemToAdjust.Upd ??= new Upd();
itemToAdjust.Upd.PinLockState = request.State;
}
public void SetFavoriteItem(PmcData pmcData, SetFavoriteItems info, string sessionId)
public void SetFavoriteItem(PmcData pmcData, SetFavoriteItems request, string sessionId)
{
throw new NotImplementedException();
// The client sends the full list of favorite items, so clear the current favorites
pmcData.Inventory.FavoriteItems = [];
pmcData.Inventory.FavoriteItems.AddRange(request.Items);
}
public void RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData info, string sessionId)
public void RedeemProfileReward(PmcData pmcData, RedeemProfileRequestData request, string sessionId)
{
throw new NotImplementedException();
var fullProfile = _profileHelper.GetFullProfile(sessionId);
foreach (var rewardEvent in request.Events)
{
// Hard coded to `SYSTEM` for now
// TODO: make this dynamic
var dialog = fullProfile.DialogueRecords["59e7125688a45068a6249071"];
var mail = dialog.Messages.FirstOrDefault(message => message.Id == rewardEvent.MessageId);
var mailEvent =
mail.ProfileChangeEvents.FirstOrDefault(changeEvent => changeEvent.Id == rewardEvent.EventId);
switch (mailEvent.Type)
{
case ProfileChangeEventType.TraderSalesSum:
pmcData.TradersInfo[mailEvent.Entity].SalesSum = mailEvent.Value;
_traderHelper.LevelUp(mailEvent.Entity, pmcData);
_logger.Success($"Set trader {mailEvent.Entity}: Sales Sum to: {mailEvent.Value}");
break;
case ProfileChangeEventType.TraderStanding:
pmcData.TradersInfo[mailEvent.Entity].Standing = mailEvent.Value;
_traderHelper.LevelUp(mailEvent.Entity, pmcData);
_logger.Success($"Set trader {mailEvent.Entity}: Standing to: {mailEvent.Value}");
break;
case ProfileChangeEventType.ProfileLevel:
pmcData.Info.Experience = mailEvent.Value.Value;
// Will calculate level below
_traderHelper.ValidateTraderStandingsAndPlayerLevelForProfile(sessionId);
_logger.Success($"Set profile xp to: {mailEvent.Value}");
break;
case ProfileChangeEventType.SkillPoints:
{
var profileSkill = pmcData.Skills.Common.FirstOrDefault(x => x.Id == mailEvent.Entity);
if (profileSkill is null)
{
_logger.Warning($"Unable to find skill with name: {mailEvent.Entity}");
continue;
}
profileSkill.Progress = mailEvent.Value;
_logger.Success($"Set profile skill: {mailEvent.Entity} to: {mailEvent.Value}");
break;
}
case ProfileChangeEventType.ExamineAllItems:
{
var itemsToInspect = _itemHelper.GetItems().Where(x => x.Type != "Node");
FlagItemsAsInspectedAndRewardXp(itemsToInspect.Select(x => x.Id), fullProfile);
_logger.Success($"Flagged {itemsToInspect.Count()} items as examined");
break;
}
case ProfileChangeEventType.UnlockTrader:
pmcData.TradersInfo[mailEvent.Entity].Unlocked = true;
_logger.Success($"Trader {mailEvent.Entity} Unlocked");
break;
case ProfileChangeEventType.AssortmentUnlockRule:
fullProfile.SptData.BlacklistedItemTemplates ??= [];
fullProfile.SptData.BlacklistedItemTemplates.Add(mailEvent.Entity);
_logger.Success($"Item {mailEvent.Entity} is now blacklisted");
break;
case ProfileChangeEventType.HideoutAreaLevel:
{
var areaName = mailEvent.Entity;
var newValue = mailEvent.Value;
var hideoutAreaType = Enum.Parse<HideoutAreas>(areaName ?? "NOTSET");
var desiredArea = pmcData.Hideout.Areas.FirstOrDefault(area => area.Type == hideoutAreaType);
if (desiredArea is not null) desiredArea.Level = newValue;
break;
}
default:
_logger.Warning($"Unhandled profile reward event: {mailEvent.Type}");
break;
}
}
}
public void OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData info, string sessionId, ItemEventRouterResponse output)
/**
* Flag an item as seen in profiles encyclopedia + add inspect xp to profile
* @param itemTpls Inspected item tpls
* @param fullProfile Profile to add xp to
*/
protected void FlagItemsAsInspectedAndRewardXp(IEnumerable<string> itemTpls, SptProfile fullProfile)
{
throw new NotImplementedException();
foreach (var itemTpl in itemTpls)
{
var item = _itemHelper.GetItem(itemTpl);
if (!item.Key)
{
_logger.Warning(
_localisationService.GetText("inventory-unable_to_inspect_item_not_in_db", itemTpl)
);
return;
}
fullProfile.CharacterData.PmcData.Info.Experience += item.Value.Properties.ExamineExperience;
fullProfile.CharacterData.PmcData.Encyclopedia[itemTpl] = false;
fullProfile.CharacterData.ScavData.Info.Experience += item.Value.Properties.ExamineExperience;
fullProfile.CharacterData.ScavData.Encyclopedia[itemTpl] = false;
}
// TODO: update this with correct calculation using values from globals json
_profileHelper.AddSkillPointsToPlayer(
fullProfile.CharacterData.PmcData,
SkillTypes.Intellect,
0.05 * itemTpls.Count()
);
}
public void EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData info, string sessionId, ItemEventRouterResponse output)
public void OpenRandomLootContainer(PmcData pmcData, OpenRandomLootContainerRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
/** Container player opened in their inventory */
var openedItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
var containerDetailsDb = _itemHelper.GetItem(openedItem.Template);
var isSealedWeaponBox = containerDetailsDb.Value.Name.Contains("event_container_airdrop");
var foundInRaid = openedItem.Upd?.SpawnedInSession;
var rewards = new List<List<Item>>();
var unlockedWeaponCrates = new List<string>
{
"665829424de4820934746ce6",
"665732e7ac60f009f270d1ef",
"665888282c4a1b73af576b77"
};
// Temp fix for unlocked weapon crate hideout craft
if (isSealedWeaponBox || unlockedWeaponCrates.Contains(containerDetailsDb.Value.Id))
{
var containerSettings = _inventoryHelper.GetInventoryConfig().SealedAirdropContainer;
rewards.AddRange(_lootGenerator.GetSealedWeaponCaseLoot(containerSettings));
if (containerSettings.FoundInRaid) foundInRaid = containerSettings.FoundInRaid;
}
else
{
var rewardContainerDetails = _inventoryHelper.GetRandomLootContainerRewardDetails(openedItem.Template);
if (rewardContainerDetails?.RewardCount == null)
{
_logger.Error($"Unable to add loot to container: {openedItem.Template}, no rewards found");
}
else
{
rewards.AddRange(_lootGenerator.GetRandomLootContainerLoot(rewardContainerDetails));
if (rewardContainerDetails.FoundInRaid) foundInRaid = rewardContainerDetails.FoundInRaid;
}
}
// Add items to player inventory
if (rewards.Count > 0)
{
var addItemsRequest = new AddItemsDirectRequest
{
ItemsWithModsToAdd = rewards,
FoundInRaid = foundInRaid,
Callback = null,
UseSortingTable = true
};
_inventoryHelper.AddItemsToStash(sessionId, addItemsRequest, pmcData, output);
if (output.Warnings.Count > 0) return;
}
// Find and delete opened container item from player inventory
_inventoryHelper.RemoveItem(pmcData, request.Item, sessionId, output);
}
public void DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData info, string sessionId, ItemEventRouterResponse output)
public void EditMapMarker(PmcData pmcData, InventoryEditMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
var mapItem = _mapMarkerService.EditMarkerOnMap(pmcData, request);
// sync with client
output.ProfileChanges[sessionId].Items.ChangedItems.Add(mapItem.ConvertToProduct());
}
public void CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData info, string sessionId, ItemEventRouterResponse output)
public void DeleteMapMarker(PmcData pmcData, InventoryDeleteMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
var mapItem = _mapMarkerService.DeleteMarkerFromMap(pmcData, request);
// sync with client
output.ProfileChanges[sessionId].Items.ChangedItems.Add(mapItem.ConvertToProduct());
}
public void SortInventory(PmcData pmcData, InventorySortRequestData info, string sessionId, ItemEventRouterResponse output)
public void CreateMapMarker(PmcData pmcData, InventoryCreateMarkerRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
var adjustedMapItem = _mapMarkerService.CreateMarkerOnMap(pmcData, request);
// Sync with client
output.ProfileChanges[sessionId].Items.ChangedItems.Add(adjustedMapItem.ConvertToProduct());
}
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData info, string sessionId)
public void SortInventory(PmcData pmcData, InventorySortRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
foreach (var change in request.ChangedItems)
{
var inventoryItem = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == change.Id);
if (inventoryItem is null)
{
_logger.Error(
_localisationService.GetText("inventory-unable_to_sort_inventory_restart_game", change.Id)
);
continue;
}
inventoryItem.ParentId = change.ParentId;
inventoryItem.SlotId = change.SlotId;
if (change.Location is not null)
inventoryItem.Location = change.Location;
else
inventoryItem.Location = null;
}
}
public void ExamineItem(PmcData pmcData, InventoryExamineRequestData info, string sessionId, ItemEventRouterResponse output)
public ItemEventRouterResponse ReadEncyclopedia(PmcData pmcData, InventoryReadEncyclopediaRequestData body,
string sessionId)
{
throw new NotImplementedException();
foreach (var id in body.Ids) pmcData.Encyclopedia[id] = true;
return _eventOutputHolder.GetOutput(sessionId);
}
public void UnBindItem(PmcData pmcData, InventoryBindRequestData info, string sessionId, ItemEventRouterResponse output)
public void ExamineItem(PmcData pmcData, InventoryExamineRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
var itemId = "";
if (request.FromOwner is not null)
{
try
{
itemId = GetExaminedItemTpl(request);
}
catch
{
_logger.Error(_localisationService.GetText("inventory-examine_item_does_not_exist", request.Item));
}
// get hideout item
if (request.FromOwner.Type == "HideoutProduction") itemId = request.Item;
}
if (itemId is null)
{
// item template
if (_databaseService.GetItems().ContainsKey(request.Item)) itemId = request.Item;
// Player inventory
var target = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
if (target is not null) itemId = target.Template;
}
if (itemId is not null)
{
var fullProfile = _profileHelper.GetFullProfile(sessionId);
FlagItemsAsInspectedAndRewardXp([itemId], fullProfile);
}
}
public void BindItem(PmcData pmcData, InventoryBindRequestData info, string sessionId, ItemEventRouterResponse output)
protected string? GetExaminedItemTpl(InventoryExamineRequestData request)
{
throw new NotImplementedException();
if (_presetHelper.IsPreset(request.Item)) return _presetHelper.GetBaseItemTpl(request.Item);
if (request.FromOwner.Id == Traders.FENCE)
// Get tpl from fence assorts
return _fenceService.GetRawFenceAssorts().Items.FirstOrDefault(x => x.Id == request.Item)?.Template;
if (request.FromOwner.Type == "Trader")
// Not fence
// get tpl from trader assort
return _databaseService
.GetTrader(request.FromOwner.Id)
.Assort.Items.FirstOrDefault(item => item.Id == request.Item)
?.Template;
if (request.FromOwner.Type == "RagFair")
{
// Try to get tplId from items.json first
var item = _itemHelper.GetItem(request.Item);
if (item.Key) return item.Value.Id;
// Try alternate way of getting offer if first approach fails
var offer = _ragfairOfferService.GetOfferByOfferId(request.Item) ??
_ragfairOfferService.GetOfferByOfferId(request.FromOwner.Id);
// Try find examine item inside offer items array
var matchingItem = offer.Items.FirstOrDefault(offerItem => offerItem.Id == request.Item);
if (matchingItem is not null) return matchingItem.Template;
// Unable to find item in database or ragfair
_logger.Warning(_localisationService.GetText("inventory-unable_to_find_item", request.Item));
}
_logger.Error($"Unable to get item with id: {request.Item}");
return null;
}
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData info, string sessionId)
public void UnBindItem(PmcData pmcData, InventoryBindRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
// Remove kvp from requested fast panel index
// TODO - does this work
pmcData.Inventory.FastPanel.Remove(request.Index.ToString());
}
public ItemEventRouterResponse ToggleItem(PmcData pmcData, InventoryToggleRequestData info, string sessionId)
public void BindItem(PmcData pmcData, InventoryBindRequestData bindRequest, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
foreach (var kvp in pmcData.Inventory.FastPanel
.Where(kvp => kvp.Value == bindRequest.Index.Value.ToString()))
{
pmcData.Inventory.FastPanel.Remove(kvp.Key);
break;
}
// Create link between fast panel slot and requested item
pmcData.Inventory.FastPanel[bindRequest.Index.ToString()] = bindRequest.Item;
}
public ItemEventRouterResponse FoldItem(PmcData pmcData, InventoryFoldRequestData info, string sessionId)
public ItemEventRouterResponse TagItem(PmcData pmcData, InventoryTagRequestData request, string sessionId)
{
throw new NotImplementedException();
var itemToTag = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == request.Item);
if (itemToTag is null)
{
_logger.Warning(
$"Unable to tag item: {request.Item} as it cannot be found in player {sessionId} inventory"
);
return new ItemEventRouterResponse { Warnings = [], ProfileChanges = { } };
}
// Null guard
itemToTag.Upd ??= new Upd();
itemToTag.Upd.Tag = new UpdTag { Color = request.TagColor, Name = request.TagName };
return _eventOutputHolder.GetOutput(sessionId);
}
public ItemEventRouterResponse SwapItem(PmcData pmcData, InventorySwapRequestData info, string sessionId)
public ItemEventRouterResponse ToggleItem(PmcData pmcData, InventoryToggleRequestData request, string sessionId)
{
throw new NotImplementedException();
// May need to reassign to scav profile
var playerData = pmcData;
// Fix for toggling items while on they're in the Scav inventory
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id)
playerData = _profileHelper.GetScavProfile(sessionId);
var itemToToggle = playerData.Inventory.Items.FirstOrDefault(x => x.Id == request.Item);
if (itemToToggle is not null)
{
_itemHelper.AddUpdObjectToItem(
itemToToggle,
_localisationService.GetText("inventory-item_to_toggle_missing_upd", itemToToggle.Id)
);
itemToToggle.Upd.Togglable = new UpdTogglable() { On = request.Value };
return _eventOutputHolder.GetOutput(sessionId);
}
_logger.Warning(_localisationService.GetText("inventory-unable_to_toggle_item_not_found", request.Item));
return new ItemEventRouterResponse { Warnings = [], ProfileChanges = { } };
}
public void TransferItem(PmcData pmcData, InventoryTransferRequestData info, string sessionId, ItemEventRouterResponse output)
public ItemEventRouterResponse FoldItem(PmcData pmcData, InventoryFoldRequestData request, string sessionId)
{
throw new NotImplementedException();
// May need to reassign to scav profile
var playerData = pmcData;
// We may be folding data on scav profile, get that profile instead
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id)
playerData = _profileHelper.GetScavProfile(sessionId);
var itemToFold = playerData.Inventory.Items.FirstOrDefault(item => item?.Id == request.Item);
if (itemToFold is null)
{
// Item not found
_logger.Warning(
_localisationService.GetText("inventory-unable_to_fold_item_not_found_in_inventory", request.Item)
);
return new ItemEventRouterResponse { Warnings = [], ProfileChanges = { } };
}
// Item may not have upd object
_itemHelper.AddUpdObjectToItem(itemToFold);
itemToFold.Upd.Foldable = new UpdFoldable { Folded = request.Value };
return _eventOutputHolder.GetOutput(sessionId);
}
public void MergeItem(PmcData pmcData, InventoryMergeRequestData info, string sessionId, ItemEventRouterResponse output)
public ItemEventRouterResponse SwapItem(PmcData pmcData, InventorySwapRequestData request, string sessionId)
{
throw new NotImplementedException();
// During post-raid scav transfer, the swap may be in the scav inventory
var playerData = pmcData;
if (request.FromOwner?.Type == "Profile" && request.FromOwner.Id != playerData.Id)
playerData = _profileHelper.GetScavProfile(sessionId);
var itemOne = playerData.Inventory.Items.FirstOrDefault(x => x.Id == request.Item);
if (itemOne is null)
_logger.Error(
_localisationService.GetText(
"inventory-unable_to_find_item_to_swap",
new
{
item1Id = request.Item,
item2Id = request.Item2
}
)
);
var itemTwo = playerData.Inventory.Items.FirstOrDefault(x => x.Id == request.Item2);
if (itemTwo is null)
_logger.Error(
_localisationService.GetText(
"inventory-unable_to_find_item_to_swap",
new
{
item1Id = request.Item2,
item2Id = request.Item
}
)
);
// to.id is the parentid
itemOne.ParentId = request.To.Id;
// to.container is the slotid
itemOne.SlotId = request.To.Container;
// Request object has location data, add it in, otherwise remove existing location from object
if (request.To.Location is not null)
itemOne.Location = request.To.Location;
else
// biome-ignore lint/performance/noDelete: Delete is fine here as we entirely want to get rid of the location.
itemOne.Location = null;
itemTwo.ParentId = request.To2.Id;
itemTwo.SlotId = request.To2.Container;
if (request.To2.Location is not null)
itemTwo.Location = request.To2.Location;
else
itemTwo.Location = null;
// Client already informed of inventory locations, nothing for us to do
return _eventOutputHolder.GetOutput(sessionId);
}
public void SplitItem(PmcData pmcData, InventorySplitRequestData info, string sessionId, ItemEventRouterResponse output)
public void TransferItem(PmcData pmcData, InventoryTransferRequestData request, string sessionId,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
// TODO - check GetOwnerInventoryItems() call still works
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(request, request.Item, sessionId);
var sourceItem = inventoryItems.From.FirstOrDefault(item => item.Id == request.Item);
var destinationItem = inventoryItems.To.FirstOrDefault(item => item.Id == request.With);
if (sourceItem is null)
{
var errorMessage = $"Unable to transfer stack, cannot find source: {request.Item}";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
if (destinationItem is not null)
{
var errorMessage = $"Unable to transfer stack, cannot find destination: {request.With}";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
sourceItem.Upd ??= new Upd { StackObjectsCount = 1 };
var sourceStackCount = sourceItem.Upd.StackObjectsCount;
if (sourceStackCount > request.Count)
// Source items stack count greater than new desired count
sourceItem.Upd.StackObjectsCount = sourceStackCount - request.Count;
else
// Moving a full stack onto a smaller stack
sourceItem.Upd.StackObjectsCount = sourceStackCount - 1;
destinationItem.Upd ??= new Upd { StackObjectsCount = 1 };
var destinationStackCount = destinationItem.Upd.StackObjectsCount;
destinationItem.Upd.StackObjectsCount = destinationStackCount + request.Count;
}
public void RemoveItem(PmcData pmcData, InventoryRemoveRequestData info, string sessionId, ItemEventRouterResponse output)
public void MergeItem(PmcData pmcData, InventoryMergeRequestData body, string sessionID,
ItemEventRouterResponse output)
{
throw new NotImplementedException();
// Changes made to result apply to character inventory
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(body, body.Item, sessionID);
// Get source item (can be from player or trader or mail)
var sourceItem = inventoryItems.From.FirstOrDefault((x) => x.Id == body.Item);
if (sourceItem is null)
{
var errorMessage = $"Unable to merge stacks as source item: {body.With} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
// Get item being merged into
var destinationItem = inventoryItems.To.FirstOrDefault((x) => x.Id == body.With);
if (destinationItem is null)
{
var errorMessage = $"Unable to merge stacks as destination item: {body.With} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
if (destinationItem.Upd?.StackObjectsCount is null)
// No stackcount on destination, add one
destinationItem.Upd = new Upd { StackObjectsCount = 1 };
if (sourceItem.Upd is null)
sourceItem.Upd = new Upd { StackObjectsCount = 1 };
else if (sourceItem.Upd.StackObjectsCount is null)
// Items pulled out of raid can have no stack count if the stack should be 1
sourceItem.Upd.StackObjectsCount = 1;
// Remove FiR status from destination stack when source stack has no FiR but destination does
if (!sourceItem.Upd.SpawnedInSession.GetValueOrDefault(false) &&
destinationItem.Upd.SpawnedInSession.GetValueOrDefault(false)) destinationItem.Upd.SpawnedInSession = false;
destinationItem.Upd.StackObjectsCount +=
sourceItem.Upd.StackObjectsCount; // Add source stackcount to destination
output.ProfileChanges[sessionID]
.Items.DeletedItems.Add(new Product { Id = sourceItem.Id }); // Inform client source item being deleted
var indexOfItemToRemove = inventoryItems.From.FindIndex((x) => x.Id == sourceItem.Id);
if (indexOfItemToRemove == -1)
{
var errorMessage = $"Unable to find item: {sourceItem.Id} to remove from sender inventory";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
inventoryItems.From.RemoveAt(indexOfItemToRemove); // Remove source item from 'from' inventory
}
public void SplitItem(PmcData pmcData, InventorySplitRequestData request, string sessionID,
ItemEventRouterResponse output)
{
// Changes made to result apply to character inventory
var inventoryItems = _inventoryHelper.GetOwnerInventoryItems(request, request.NewItem, sessionID);
// Handle cartridge edge-case
if (request.Container.Location is null && request.Container.ContainerName == "cartridges")
{
var matchingItems = inventoryItems.To.Where((x) => x.ParentId == request.Container.Id);
request.Container.Location = matchingItems.Count(); // Wrong location for first cartridge
}
// The item being merged has three possible sources: pmc, scav or mail, getOwnerInventoryItems() handles getting correct one
var itemToSplit = inventoryItems.From.FirstOrDefault((x) => x.Id == request.SplitItem);
if (itemToSplit is null)
{
var errorMessage = $"Unable to split stack as source item: {request.SplitItem} cannot be found";
_logger.Error(errorMessage);
_httpResponseUtil.AppendErrorToOutput(output, errorMessage);
return;
}
// Create new upd object that retains properties of original upd + new stack count size
var updatedUpd = _cloner.Clone(itemToSplit.Upd);
updatedUpd.StackObjectsCount = request.Count;
// Remove split item count from source stack
itemToSplit.Upd.StackObjectsCount -= request.Count;
// Inform client of change
output.ProfileChanges[sessionID]
.Items.NewItems.Add(
new Product
{
Id = request.NewItem,
Template = itemToSplit.Template,
Upd = updatedUpd
}
);
// Update player inventory
inventoryItems.To.Add(
new Item
{
Id = request.NewItem,
Template = itemToSplit.Template,
ParentId = request.Container.Id,
SlotId = request.Container.ContainerName,
Location = request.Container.Location,
Upd = updatedUpd
}
);
}
public void RemoveItem(PmcData pmcData, InventoryRemoveRequestData request, string sessionId,
ItemEventRouterResponse output)
{
if (request.FromOwner?.Type == "Mail")
{
_inventoryHelper.RemoveItemAndChildrenFromMailRewards(sessionId, request, output);
return;
}
var profileToRemoveItemFrom = request?.FromOwner.Id == pmcData.Id
? pmcData
: _profileHelper.GetFullProfile(sessionId).CharacterData.ScavData;
_inventoryHelper.RemoveItem(profileToRemoveItemFrom, request.Item, sessionId, output);
}
}
@@ -1,5 +1,4 @@
using SptCommon.Annotations;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Launcher;
using Core.Models.Spt.Config;
+168 -133
View File
@@ -1,26 +1,30 @@
using System.Text.Json.Serialization;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Inventory;
using Core.Models.Eft.ItemEvent;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Spt.Inventory;
using Core.Models.Utils;
using Core.Servers;
using Core.Services;
using Core.Models.Eft.Player;
using System.ComponentModel;
using Core.Models.Eft.Hideout;
using Core.Models.Enums;
using Core.Models.Spt.Bots;
using Core.Models.Spt.Services;
using System.Xml.Linq;
using Core.Utils;
using SptCommon.Annotations;
namespace Core.Helpers;
[Injectable]
public class InventoryHelper(
ISptLogger<InventoryHelper> _logger,
HashUtil hashUtil,
HttpResponseUtil httpResponseUtil,
FenceService fenceService,
DialogueHelper dialogueHelper,
ContainerHelper containerHelper,
DatabaseServer databaseServer,
PaymentHelper paymentHelper,
TraderAssortHelper traderAssortHelper,
ProfileHelper _profileHelper,
DialogueHelper _dialogueHelper,
ContainerHelper _containerHelper,
@@ -29,7 +33,7 @@ public class InventoryHelper(
)
{
/// <summary>
/// Add multiple items to player stash (assuming they all fit)
/// Add multiple items to player stash (assuming they all fit)
/// </summary>
/// <param name="sessionId">Session id</param>
/// <param name="request">AddItemsDirectRequest request</param>
@@ -45,7 +49,7 @@ public class InventoryHelper(
}
/// <summary>
/// Add whatever is passed in request.itemWithModsToAdd into player inventory (if it fits)
/// Add whatever is passed in request.itemWithModsToAdd into player inventory (if it fits)
/// </summary>
/// <param name="sessionId">Session id</param>
/// <param name="request">AddItemDirect request</param>
@@ -61,7 +65,7 @@ public class InventoryHelper(
}
/// <summary>
/// Set FiR status for an item + its children
/// Set FiR status for an item + its children
/// </summary>
/// <param name="itemWithChildren">An item</param>
/// <param name="foundInRaid">Item was found in raid</param>
@@ -71,7 +75,7 @@ public class InventoryHelper(
}
/// <summary>
/// Remove properties from a Upd object used by a trader/ragfair that are unnecessary to a player
/// Remove properties from a Upd object used by a trader/ragfair that are unnecessary to a player
/// </summary>
/// <param name="upd">Object to update</param>
protected void RemoveTraderRagfairRelatedUpdProperties(Upd upd)
@@ -80,7 +84,7 @@ public class InventoryHelper(
}
/// <summary>
/// Can all provided items be added into player inventory
/// Can all provided items be added into player inventory
/// </summary>
/// <param name="sessionId">Player id</param>
/// <param name="itemsWithChildren">Array of items with children to try and fit</param>
@@ -91,7 +95,7 @@ public class InventoryHelper(
}
/// <summary>
/// Do the provided items all fit into the grid
/// Do the provided items all fit into the grid
/// </summary>
/// <param name="containerFS2D">Container grid to fit items into</param>
/// <param name="itemsWithChildren">Items to try and fit into grid</param>
@@ -102,7 +106,7 @@ public class InventoryHelper(
}
/// <summary>
/// Does an item fit into a container grid
/// Does an item fit into a container grid
/// </summary>
/// <param name="containerFS2D">Container grid</param>
/// <param name="itemWithChildren">Item to check fits</param>
@@ -125,7 +129,8 @@ public class InventoryHelper(
findSlotResult.Y.Value,
itemSize[0],
itemSize[1],
findSlotResult.Rotation.Value);
findSlotResult.Rotation.Value
);
}
catch (Exception ex)
{
@@ -142,7 +147,7 @@ public class InventoryHelper(
}
/// <summary>
/// Find a free location inside a container to fit the item
/// Find a free location inside a container to fit the item
/// </summary>
/// <param name="containerFS2D">Container grid to add item to</param>
/// <param name="itemWithChildren">Item to add to grid</param>
@@ -170,7 +175,8 @@ public class InventoryHelper(
findSlotResult.Y.Value,
itemSize[0],
itemSize[1],
findSlotResult.Rotation.Value);
findSlotResult.Rotation.Value
);
}
catch (Exception ex)
{
@@ -178,6 +184,7 @@ public class InventoryHelper(
return;
}
// Store details for object, incuding container item will be placed in
rootItemAdded.ParentId = containerId;
rootItemAdded.SlotId = desiredSlotId;
@@ -186,16 +193,15 @@ public class InventoryHelper(
X = findSlotResult.X,
Y = findSlotResult.Y,
R = findSlotResult.Rotation.GetValueOrDefault(false) ? 1 : 0,
Rotation = findSlotResult.Rotation,
Rotation = findSlotResult.Rotation
};
// Success! exit
return;
}
}
/// <summary>
/// Find a location to place an item into inventory and place it
/// Find a location to place an item into inventory and place it
/// </summary>
/// <param name="stashFS2D">2-dimensional representation of the container slots</param>
/// <param name="sortingTableFS2D">2-dimensional representation of the sorting table slots</param>
@@ -215,9 +221,9 @@ public class InventoryHelper(
}
/// <summary>
/// Handle Remove event
/// Remove item from player inventory + insured items array
/// Also deletes child items
/// Handle Remove event
/// Remove item from player inventory + insured items array
/// Also deletes child items
/// </summary>
/// <param name="profile">Profile to remove item from (pmc or scav)</param>
/// <param name="itemId">Items id to remove</param>
@@ -229,18 +235,19 @@ public class InventoryHelper(
}
/// <summary>
/// Delete desired item from a player profiles mail
/// Delete desired item from a player profiles mail
/// </summary>
/// <param name="sessionId">Session id</param>
/// <param name="removeRequest">Remove request</param>
/// <param name="output">OPTIONAL - ItemEventRouterResponse</param>
public void RemoveItemAndChildrenFromMailRewards(string sessionId, InventoryRemoveRequestData removeRequest, ItemEventRouterResponse output = null)
public void RemoveItemAndChildrenFromMailRewards(string sessionId, InventoryRemoveRequestData removeRequest,
ItemEventRouterResponse output = null)
{
throw new NotImplementedException();
}
/// <summary>
/// Find item by id in player inventory and remove x of its count
/// Find item by id in player inventory and remove x of its count
/// </summary>
/// <param name="pmcData">player profile</param>
/// <param name="itemId">Item id to decrement StackObjectsCount of</param>
@@ -248,13 +255,14 @@ public class InventoryHelper(
/// <param name="sessionId">Session id</param>
/// <param name="output">ItemEventRouterResponse</param>
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse RemoveItemByCount(PmcData pmcData, string itemId, int countToRemove, string sessionId, ItemEventRouterResponse output = null)
public ItemEventRouterResponse RemoveItemByCount(PmcData pmcData, string itemId, int countToRemove,
string sessionId, ItemEventRouterResponse output = null)
{
throw new NotImplementedException();
}
/// <summary>
/// Get the height and width of an item - can have children that alter size
/// Get the height and width of an item - can have children that alter size
/// </summary>
/// <param name="itemTpl">Item to get size of</param>
/// <param name="itemId">Items id to get size of</param>
@@ -267,8 +275,8 @@ public class InventoryHelper(
}
/// <summary>
/// Calculates the size of an item including attachments
/// takes into account if item is folded
/// Calculates the size of an item including attachments
/// takes into account if item is folded
/// </summary>
/// <param name="itemTpl">Items template id</param>
/// <param name="itemId">Items id</param>
@@ -280,19 +288,18 @@ public class InventoryHelper(
var (key, tmpItem) = _itemHelper.GetItem(itemTpl);
// Invalid item
if (!key)
{
_logger.Error(_localisationService.GetText("inventory-invalid_item_missing_from_db", itemTpl));
}
if (!key) _logger.Error(_localisationService.GetText("inventory-invalid_item_missing_from_db", itemTpl));
// Item found but no _props property
if (key && tmpItem.Properties is null)
{
_localisationService.GetText("inventory-item_missing_props_property", new {
itemTpl = itemTpl,
itemName = tmpItem?.Name
});
}
_localisationService.GetText(
"inventory-item_missing_props_property",
new
{
itemTpl,
itemName = tmpItem?.Name
}
);
// No item object or getItem() returned false
if (!key && tmpItem is null)
@@ -320,52 +327,44 @@ public class InventoryHelper(
var outY = (int)tmpItem.Properties.Height;
// Item types to ignore
var skipThisItems = new List<string> { BaseClasses.BACKPACK, BaseClasses.SEARCHABLE_ITEM, BaseClasses.SIMPLE_CONTAINER };
var skipThisItems = new List<string>
{ BaseClasses.BACKPACK, BaseClasses.SEARCHABLE_ITEM, BaseClasses.SIMPLE_CONTAINER };
var rootIsFolded = rootItem?.Upd?.Foldable?.Folded == true;
// The item itself is collapsible
if (isFoldable is not null && string.IsNullOrEmpty(foldedSlot) && rootIsFolded)
{
outX -= tmpItem.Properties.SizeReduceRight.Value;
}
// Calculate size contribution from child items/attachments
if (!skipThisItems.Contains(tmpItem.Parent))
{
while (toDo.Count > 0)
{
if (inventoryItemHash.ByParentId.ContainsKey(toDo[0])) {
foreach (var item in inventoryItemHash.ByParentId[toDo[0]]) {
if (inventoryItemHash.ByParentId.ContainsKey(toDo[0]))
foreach (var item in inventoryItemHash.ByParentId[toDo[0]])
{
// Filtering child items outside of mod slots, such as those inside containers, without counting their ExtraSize attribute
if (item.SlotId.IndexOf("mod_") < 0)
{
continue;
}
if (item.SlotId.IndexOf("mod_") < 0) continue;
toDo.Add(item.Id);
// If the barrel is folded the space in the barrel is not counted
var itemResult = _itemHelper.GetItem(item.Template);
if (!itemResult.Key)
{
_logger.Error(
_localisationService.GetText("inventory-get_item_size_item_not_found_by_tpl", item.Template));
}
_localisationService.GetText(
"inventory-get_item_size_item_not_found_by_tpl",
item.Template
)
);
var itm = itemResult.Value;
var childFoldable = itm.Properties.Foldable.GetValueOrDefault(false);
var childFolded = item.Upd?.Foldable is not null && item.Upd.Foldable.Folded == true;
if (isFoldable is true && foldedSlot == item.SlotId && (rootIsFolded || childFolded))
{
continue;
}
if (isFoldable is true && foldedSlot == item.SlotId && (rootIsFolded || childFolded)) continue;
if (childFoldable && rootIsFolded && childFolded)
{
continue;
}
if (childFoldable && rootIsFolded && childFolded) continue;
// Calculating child ExtraSize
if (itm.Properties.ExtraSizeForceAdd == true)
@@ -378,25 +377,30 @@ public class InventoryHelper(
else
{
sizeUp = sizeUp < itm.Properties.ExtraSizeUp ? itm.Properties.ExtraSizeUp.Value : sizeUp;
sizeDown = sizeDown < itm.Properties.ExtraSizeDown ? itm.Properties.ExtraSizeDown.Value : sizeDown;
sizeLeft = sizeLeft < itm.Properties.ExtraSizeLeft ? itm.Properties.ExtraSizeLeft.Value : sizeLeft;
sizeRight = sizeRight < itm.Properties.ExtraSizeRight ? itm.Properties.ExtraSizeRight.Value : sizeRight;
sizeDown = sizeDown < itm.Properties.ExtraSizeDown
? itm.Properties.ExtraSizeDown.Value
: sizeDown;
sizeLeft = sizeLeft < itm.Properties.ExtraSizeLeft
? itm.Properties.ExtraSizeLeft.Value
: sizeLeft;
sizeRight = sizeRight < itm.Properties.ExtraSizeRight
? itm.Properties.ExtraSizeRight.Value
: sizeRight;
}
}
}
toDo.RemoveAt(0);
}
}
return [
return
[
outX + sizeLeft + sizeRight + forcedLeft + forcedRight,
outY + sizeUp + sizeDown + forcedUp + forcedDown,
outY + sizeUp + sizeDown + forcedUp + forcedDown
];
}
/// <summary>
/// Get a blank two-dimensional representation of a container
/// Get a blank two-dimensional representation of a container
/// </summary>
/// <param name="containerH">Horizontal size of container</param>
/// <param name="containerY">Vertical size of container</param>
@@ -417,7 +421,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get a 2d mapping of a container with what grid slots are filled
/// Get a 2d mapping of a container with what grid slots are filled
/// </summary>
/// <param name="containerH">Horizontal size of container</param>
/// <param name="containerV">Vertical size of container</param>
@@ -434,13 +438,12 @@ public class InventoryHelper(
// Get subset of items that belong to the desired container
if (!inventoryItemHash.ByParentId.TryGetValue(containerId, out List<Item> containerItemHash))
{
// No items in container, exit early
return container2D;
}
// Check each item in container
foreach (var item in containerItemHash) {
foreach (var item in containerItemHash)
{
var itemLocation = item?.Location as ItemLocation;
if (itemLocation is null)
{
@@ -461,28 +464,29 @@ public class InventoryHelper(
var fillTo = itemLocation.X + fW;
for (var y = 0; y < fH; y++)
{
try
{
var rowIndex = itemLocation.Y + y;
var containerRow = container2D[rowIndex.Value];
if (containerRow is null)
{
_logger.Error("Unable to find container: { containerId} row line: { itemLocation.y + y}");
}
// Fill the corresponding cells in the container map to show the slot is taken
Array.Fill(containerRow, 1, itemLocation.X.Value, fW);
} catch (Exception ex) {
}
catch (Exception ex)
{
_logger.Error(
_localisationService.GetText("inventory-unable_to_fill_container", new {
id = item.Id,
error = $"{ex.Message} {ex.StackTrace}"
})
_localisationService.GetText(
"inventory-unable_to_fill_container",
new
{
id = item.Id,
error = $"{ex.Message} {ex.StackTrace}"
}
)
);
}
}
}
return container2D;
@@ -498,29 +502,27 @@ public class InventoryHelper(
{
var inventoryItemHash = new InventoryItemHash
{
ByItemId = new(),
ByParentId = new()
ByItemId = new Dictionary<string, Item>(),
ByParentId = new Dictionary<string, List<Item>>()
};
foreach (var item in inventoryItems)
{
inventoryItemHash.ByItemId.TryAdd(item.Id, item);
if (item.ParentId is null) {
continue;
}
if (item.ParentId is null) continue;
if (!inventoryItemHash.ByParentId.ContainsKey(item.ParentId)) {
if (!inventoryItemHash.ByParentId.ContainsKey(item.ParentId))
inventoryItemHash.ByParentId[item.ParentId] = [];
}
inventoryItemHash.ByParentId[item.ParentId].Add(item);
}
return inventoryItemHash;
}
/// <summary>
/// Return the inventory that needs to be modified (scav/pmc etc)
/// Changes made to result apply to character inventory
/// Based on the item action, determine whose inventories we should be looking at for from and to.
/// Return the inventory that needs to be modified (scav/pmc etc)
/// Changes made to result apply to character inventory
/// Based on the item action, determine whose inventories we should be looking at for from and to.
/// </summary>
/// <param name="request">Item interaction request</param>
/// <param name="item">Item being moved/split/etc to inventory</param>
@@ -571,13 +573,13 @@ public class InventoryHelper(
From = fromInventoryItems,
To = toInventoryItems,
SameInventory = movingToSameInventory,
IsMail = fromType == "mail",
IsMail = fromType == "mail"
};
}
/// <summary>
/// Get a two-dimensional array to represent stash slots
/// 0 value = free, 1 = taken
/// Get a two-dimensional array to represent stash slots
/// 0 value = free, 1 = taken
/// </summary>
/// <param name="pmcData">Player profile</param>
/// <param name="sessionID">session id</param>
@@ -588,7 +590,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get a blank two-dimensional array representation of a container
/// Get a blank two-dimensional array representation of a container
/// </summary>
/// <param name="containerTpl">Container to get data for</param>
/// <returns>blank two-dimensional array</returns>
@@ -604,7 +606,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get a two-dimensional array representation of the players sorting table
/// Get a two-dimensional array representation of the players sorting table
/// </summary>
/// <param name="pmcData">Player profile</param>
/// <returns>two-dimensional array</returns>
@@ -614,7 +616,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get Players Stash Size
/// Get Players Stash Size
/// </summary>
/// <param name="sessionID">Players id</param>
/// <returns>Dictionary of 2 values, horizontal and vertical stash size</returns>
@@ -624,7 +626,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get the players stash items tpl
/// Get the players stash items tpl
/// </summary>
/// <param name="sessionID">Player id</param>
/// <returns>Stash tpl</returns>
@@ -634,18 +636,54 @@ public class InventoryHelper(
}
/// <summary>
/// Internal helper function to transfer an item + children from one profile to another.
/// Internal helper function to transfer an item + children from one profile to another.
/// </summary>
/// <param name="sourceItems">Inventory of the source (can be non-player)</param>
/// <param name="toItems">Inventory of the destination</param>
/// <param name="request">Move request</param>
public void MoveItemToProfile(List<Item> sourceItems, List<Item> toItems, InventoryMoveRequestData request)
{
throw new NotImplementedException();
HandleCartridges(sourceItems, request);
// Get all children item has, they need to move with item
var idsToMove = _itemHelper.FindAndReturnChildrenByItems(sourceItems, request.Item);
foreach (var itemId in idsToMove) {
var itemToMove = sourceItems.FirstOrDefault((item) => item.Id == itemId);
if (itemToMove is null)
{
_logger.Error(_localisationService.GetText("inventory-unable_to_find_item_to_move", itemId));
continue;
}
// Only adjust the values for parent item, not children (their values are already correctly tied to parent)
if (itemId == request.Item)
{
itemToMove.ParentId = request.To.Id;
itemToMove.SlotId = request.To.Container;
if (request.To.Location is not null)
{
// Update location object
itemToMove.Location = request.To.Location;
}
else
{
// No location in request, delete it
if (itemToMove.Location is null)
{
// biome-ignore lint/performance/noDelete: Delete is fine here as we're trying to remove the entire data property.
itemToMove.Location = null;
}
}
}
toItems.Add(itemToMove);
sourceItems.RemoveAt(sourceItems.IndexOf(itemToMove));
}
}
/// <summary>
/// Internal helper function to move item within the same profile.
/// Internal helper function to move item within the same profile.
/// </summary>
/// <param name="pmcData">profile to edit</param>
/// <param name="inventoryItems"></param>
@@ -662,7 +700,7 @@ public class InventoryHelper(
HandleCartridges(inventoryItems, moveRequest);
// Find item we want to 'move'
var matchingInventoryItem = inventoryItems.FirstOrDefault((item) => item.Id == moveRequest.Item);
var matchingInventoryItem = inventoryItems.FirstOrDefault(item => item.Id == moveRequest.Item);
if (matchingInventoryItem is null)
{
var noMatchingItemMesage = $"Unable to move item: {moveRequest.Item}, cannot find in inventory";
@@ -673,7 +711,7 @@ public class InventoryHelper(
}
_logger.Debug(
$"{ moveRequest.Action} item: ${ moveRequest.Item} from slotid: { matchingInventoryItem.SlotId} to container: { moveRequest.To.Container}"
$"{moveRequest.Action} item: ${moveRequest.Item} from slotid: {matchingInventoryItem.SlotId} to container: {moveRequest.To.Container}"
);
// Don't move shells from camora to cartridges (happens when loading shells into mts-255 revolver shotgun)
@@ -685,7 +723,7 @@ public class InventoryHelper(
new
{
slotId = matchingInventoryItem.SlotId,
container = moveRequest.To.Container,
container = moveRequest.To.Container
}
)
);
@@ -708,17 +746,14 @@ public class InventoryHelper(
else
{
// Moved from slot with location to one without, clean up
if (matchingInventoryItem.Location is not null)
{
matchingInventoryItem.Location = null;
}
if (matchingInventoryItem.Location is not null) matchingInventoryItem.Location = null;
}
return true;
}
/// <summary>
/// Update fast panel bindings when an item is moved into a container that doesn't allow quick slot access
/// Update fast panel bindings when an item is moved into a container that doesn't allow quick slot access
/// </summary>
/// <param name="pmcData">Player profile</param>
/// <param name="itemBeingMoved">item being moved</param>
@@ -726,39 +761,39 @@ public class InventoryHelper(
{
// Find matching _id in fast panel
if (!pmcData.Inventory.FastPanel.TryGetValue(itemBeingMoved.Id, out var fastPanelSlot))
{
return;
}
if (!pmcData.Inventory.FastPanel.TryGetValue(itemBeingMoved.Id, out var fastPanelSlot)) return;
// Get moved items parent (should be container item was put into)
var itemParent = pmcData.Inventory.Items.FirstOrDefault((item) => item.Id == itemBeingMoved.ParentId);
if (itemParent is null)
{
return;
}
var itemParent = pmcData.Inventory.Items.FirstOrDefault(item => item.Id == itemBeingMoved.ParentId);
if (itemParent is null) return;
// Reset fast panel value if item was moved to a container other than pocket/rig (cant be used from fastpanel)
List<string> slots = ["pockets", "tacticalvest"];
var wasMovedToFastPanelAccessibleContainer = slots.Contains(
itemParent?.SlotId?.ToLower() ?? ""
);
if (!wasMovedToFastPanelAccessibleContainer)
{
pmcData.Inventory.FastPanel[fastPanelSlot[0].ToString()] = "";
}
if (!wasMovedToFastPanelAccessibleContainer) pmcData.Inventory.FastPanel[fastPanelSlot[0].ToString()] = "";
}
/// <summary>
/// Internal helper function to handle cartridges in inventory if any of them exist.
/// Internal helper function to handle cartridges in inventory if any of them exist.
/// </summary>
protected void HandleCartridges(List<Item> items, InventoryMoveRequestData request)
{
throw new NotImplementedException();
// Not moving item into a cartridge slot, skip
if (request.To.Container != "cartridges")
{
return;
}
// Get a count of cartridges in existing magazine
var cartridgeCount = items.Count(item => item.ParentId == request.To.Id);
request.To.Location = cartridgeCount;
}
/// <summary>
/// Get details for how a random loot container should be handled, max rewards, possible reward tpls
/// Get details for how a random loot container should be handled, max rewards, possible reward tpls
/// </summary>
/// <param name="itemTpl">Container being opened</param>
/// <returns>Reward details</returns>
@@ -768,7 +803,7 @@ public class InventoryHelper(
}
/// <summary>
/// Get inventory configuration
/// Get inventory configuration
/// </summary>
/// <returns>Inventory configuration</returns>
public InventoryConfig GetInventoryConfig()
@@ -777,9 +812,9 @@ public class InventoryHelper(
}
/// <summary>
/// Recursively checks if the given item is
/// inside the stash, that is it has the stash as
/// ancestor with slotId=hideout
/// Recursively checks if the given item is
/// inside the stash, that is it has the stash as
/// ancestor with slotId=hideout
/// </summary>
/// <param name="pmcData">Player profile</param>
/// <param name="itemToCheck">Item to look for</param>
@@ -795,7 +830,7 @@ public class InventoryHelper(
}
/// <summary>
/// Does the provided item have a root item with the provided id
/// Does the provided item have a root item with the provided id
/// </summary>
/// <param name="pmcData">Profile with items</param>
/// <param name="item">Item to check</param>
@@ -25,6 +25,20 @@ public record Item
[JsonPropertyName("upd")]
public Upd? Upd { get; set; }
public ItemEvent.Product ConvertToProduct()
{
// TODO - maybe this entire product item can be replaced with Item?
return new ItemEvent.Product
{
Id = Id,
Template = Template,
ParentId = ParentId,
SlotId = SlotId,
Upd = Upd,
Location = (ItemLocation)Location
};
}
}
public record ItemLocation
@@ -137,8 +137,9 @@ public record Props
[JsonPropertyName("LootExperience")]
public double? LootExperience { get; set; }
// Checked on live
[JsonPropertyName("ExamineExperience")]
public double? ExamineExperience { get; set; }
public int? ExamineExperience { get; set; }
[JsonPropertyName("HideEntrails")]
public bool? HideEntrails { get; set; }
@@ -1,9 +1,9 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Core.Models.Eft.Inventory;
public record InventoryReadEncyclopediaRequestData : InventoryBaseActionRequestData
{
[JsonPropertyName("ids")]
public List<string>? Ids { get; set; }
public List<string> Ids { get; set; }
}
@@ -1,11 +1,11 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace Core.Models.Eft.Inventory;
public record SetFavoriteItems : InventoryBaseActionRequestData
{
[JsonPropertyName("items")]
public List<object>? Items { get; set; }
public List<string>? Items { get; set; }
[JsonPropertyName("timestamp")]
public long? Timestamp { get; set; }
@@ -93,7 +93,7 @@ public record ProfileChangeEvent
public ProfileChangeEventType? Type { get; set; }
[JsonPropertyName("value")]
public double? Value { get; set; }
public int? Value { get; set; }
[JsonPropertyName("entity")]
public string? Entity { get; set; }
@@ -16,7 +16,7 @@ public class RagfairOfferService
throw new NotImplementedException();
}
public RagfairOffer GetOfferByOfferId(string offerId)
public RagfairOffer? GetOfferByOfferId(string offerId)
{
throw new NotImplementedException();
}