private static bool SharedValidateQueueIsNotFull( ICharacter character, RecipeWithSkin recipeEntry, ushort countToCraft, ushort maxCraftingQueueEntriesCount) { var characterServerState = PlayerCharacter.GetPrivateState(character); var craftingQueueItems = characterServerState.CraftingQueue.QueueItems; if (craftingQueueItems.Count + 1 <= maxCraftingQueueEntriesCount) { // allow to add the recipe return(true); } foreach (var scheduledQueueItem in craftingQueueItems) { var macCountToCraft = recipeEntry.Recipe.OutputItems.Items[0].ProtoItem.MaxItemsPerStack; if (scheduledQueueItem.CanCombineWith(recipeEntry) && (int)scheduledQueueItem.CountToCraftRemains + (int)countToCraft <= macCountToCraft) { // allow to increase already queued recipe's count return(true); } } Logger.Info("Crafting queue is full", character); if (IsClient) { ClientShowNotificationCraftingQueueIsFull(); } return(false); }
public bool CanCombineWith(RecipeWithSkin recipeEntry) { if (recipeEntry.Recipe != this.RecipeEntry.Recipe || recipeEntry.ProtoItemSkinOverride is not null || this.RecipeEntry.ProtoItemSkinOverride is not null) { return(false); } foreach (var outputItem in this.RecipeEntry.Recipe.OutputItems.Items) { if (!outputItem.ProtoItem.IsStackable) { return(false); } } return(true); }
public static async Task ClientStartCrafting(RecipeWithSkin recipeEntry, ushort countToCraft) { recipeEntry.Validate(); if (recipeEntry.ProtoItemSkinOverride is IProtoItemWithSkinData protoItemSkin) { if (!Client.Microtransactions.IsSkinOwned((ushort)protoItemSkin.SkinId)) { throw new Exception("The skin is not owned: " + protoItemSkin); } } if (SharedValidateQueueIsNotFull(Client.Characters.CurrentPlayerCharacter, recipeEntry, countToCraft, maxCraftingQueueEntriesCount: ClientCurrentMaxCraftingQueueEntriesCount)) { await Instance.CallServer(_ => _.ServerRemote_CraftRecipe(recipeEntry, countToCraft)); } }
/// <summary> /// Enqueue crafting selected recipe. /// </summary> /// <param name="craftingQueue">Crafting queue instance.</param> /// <param name="recipeEntry">Recipe instance.</param> /// <param name="countToCraft">Count to craft - must be greater than zero.</param> public static void ServerStartCrafting( [CanBeNull] IStaticWorldObject station, [CanBeNull] ICharacter character, [NotNull] CraftingQueue craftingQueue, [NotNull] RecipeWithSkin recipeEntry, ushort countToCraft, ushort?maxQueueSize = null) { if (station is null && character is null) { throw new NullReferenceException("Character AND station cannot be null simultaneously"); } if (countToCraft == 0) { throw new Exception("Are you really want to craft zero items?"); } var isAdminMode = character is not null && CreativeModeSystem.SharedIsInCreativeMode(character); if (!isAdminMode && recipeEntry.Recipe.RecipeType != RecipeType.ManufacturingByproduct && !recipeEntry.Recipe.CanBeCrafted(character, station, craftingQueue, countToCraft: recipeEntry.Recipe.RecipeType == RecipeType.Manufacturing ? (ushort)1 : countToCraft)) { Logger.Error($"Recipe cannot be crafted - check failed: {recipeEntry} at {station}.", character); return; } var queueCount = craftingQueue.QueueItems.Count; if (queueCount > 0) { foreach (var existingQueueItem in craftingQueue.QueueItems) { if (!existingQueueItem.CanCombineWith(recipeEntry)) { continue; } // try to increase the count to craft var lastQueueItemCountBefore = existingQueueItem.CountToCraftRemains; var maxCountToCraft = ushort.MaxValue; var isManufacturingRecipe = recipeEntry.Recipe.RecipeType != RecipeType.Hand && recipeEntry.Recipe.RecipeType != RecipeType.StationCrafting; if (!isManufacturingRecipe) { maxCountToCraft = recipeEntry.Recipe.OutputItems.Items[0].ProtoItem.MaxItemsPerStack; } var originalRequestedCountToCraft = countToCraft; var lastQueueItemCountNew = (ushort)Math.Min(maxCountToCraft, lastQueueItemCountBefore + countToCraft); var canAddCount = lastQueueItemCountNew - lastQueueItemCountBefore; if (canAddCount > 0) { existingQueueItem.CountToCraftRemains = lastQueueItemCountNew; Logger.Info( $"Recipe count extended for crafting: {recipeEntry} at {station}: from x{lastQueueItemCountBefore} to x{countToCraft}", character); ServerDestroyInputItems(craftingQueue, recipeEntry.Recipe, countToCraft: (ushort)canAddCount, isAdminMode); if (isManufacturingRecipe) { return; } var remainingCountToCraft = originalRequestedCountToCraft - canAddCount; if (remainingCountToCraft <= 0) { // the last queue item took all the requested items count to craft return; } // last queue item cannot accomodate all the requested count to craft // let's try to add to previous queue item or a new queue item countToCraft = (ushort)remainingCountToCraft; } } } if (maxQueueSize.HasValue && craftingQueue.QueueItems.Count >= maxQueueSize.Value) { Logger.Info( $"Recipe cannot be queue for crafting due to max queue size limitation: {recipeEntry} at {station} with max queue size {maxQueueSize.Value}.", character); return; } var queueItem = new CraftingQueueItem(recipeEntry, countToCraft, craftingQueue.ServerLastQueueItemLocalId++); ServerDestroyInputItems(craftingQueue, recipeEntry.Recipe, countToCraft: queueItem.CountToCraftRemains, isAdminMode); craftingQueue.QueueItems.Add(queueItem); if (craftingQueue.QueueItems.Count == 1) { // the added recipe is first in queue - so we will craft it right now craftingQueue.SetDurationFromCurrentRecipe(); } Logger.Info($"Recipe queued for crafting: {recipeEntry} at {station}.", character); }
public CraftingQueueItem(RecipeWithSkin recipeEntry, ushort countToCraft, ushort localId) { this.RecipeEntry = recipeEntry; this.CountToCraftRemains = countToCraft; this.LocalId = localId; }
public bool ServerRemote_CraftRecipe(RecipeWithSkin recipeEntry, ushort countToCraft) { recipeEntry.Validate(); var character = ServerRemoteContext.Character; var characterServerState = PlayerCharacter.GetPrivateState(character); if (recipeEntry.ProtoItemSkinOverride is IProtoItemWithSkinData protoItemSkin) { if (!Server.Items.IsSkinOwned(character, (ushort)protoItemSkin.SkinId)) { throw new Exception("The skin is not owned: " + protoItemSkin + " for " + character); } } IStaticWorldObject station; var craftingQueue = characterServerState.CraftingQueue; switch (recipeEntry.Recipe) { case Recipe.RecipeForHandCrafting: // simply craft by character station = null; break; case Recipe.RecipeForStationCrafting recipeForStation: station = SharedFindNearbyStationOfTypes(recipeForStation.StationTypes, character); if (station is null) { Logger.Error( $"No crafting stations of types {recipeForStation.StationTypes.GetJoinedString()} found nearby character {character} at position {character.Position}"); return(false); } break; default: throw new Exception("Incorrect recipe for in-hand or station crafting: " + recipeEntry); } // extra check (it's also done in the recipe itself) if (!recipeEntry.Recipe.SharedIsTechUnlocked(character)) { // locked recipe return(false); } var maxCraftingQueueEntriesCount = SharedGetMaxCraftingQueueEntriesCount(character); if (recipeEntry.Recipe.OutputItems.Items[0].ProtoItem.IsStackable) { // stackable items if (!SharedValidateQueueIsNotFull(character, recipeEntry, countToCraft, maxCraftingQueueEntriesCount)) { return(false); } CraftingMechanics.ServerStartCrafting(station, character, craftingQueue, recipeEntry, countToCraft, maxQueueSize: maxCraftingQueueEntriesCount); } else { // non-stackable items countToCraft = MathHelper.Clamp(countToCraft, min: (ushort)1, max: maxCraftingQueueEntriesCount); for (var i = 0; i < countToCraft; i++) { if (!SharedValidateQueueIsNotFull(character, recipeEntry, countToCraft: 1, maxCraftingQueueEntriesCount)) { return(false); } CraftingMechanics.ServerStartCrafting(station, character, craftingQueue, recipeEntry, countToCraft: 1, maxQueueSize: maxCraftingQueueEntriesCount); } } return(true); }