5 using System.Collections.Generic;
6 using System.Collections.Immutable;
14 private float progressTimer;
15 private float progressState;
17 private bool hasPower;
21 private float userDeconstructorSpeedMultiplier = 1.0f;
23 private const float TinkeringSpeedIncrease = 2.5f;
29 get {
return inputContainer; }
34 get {
return outputContainer; }
52 InitProjSpecific(element);
55 partial
void InitProjSpecific(XElement element);
61 if (containers.Count < 2)
63 DebugConsole.ThrowError(
"Error in item \"" +
item.
Name +
"\": Deconstructors must have two ItemContainer components!");
67 inputContainer = containers[0];
68 outputContainer = containers[1];
71 Identifier eventIdentifier =
new Identifier(nameof(
Deconstructor));
75 OnItemLoadedProjSpecific();
78 partial
void OnItemLoadedProjSpecific();
93 if (!hasPower) {
return; }
96 if (repairable !=
null)
105 float tinkeringStrength = 0f;
106 if (repairable.IsTinkering)
108 tinkeringStrength = repairable.TinkeringStrength;
111 float deconstructionSpeedModifier = userDeconstructorSpeedMultiplier * (1f + tinkeringStrength * TinkeringSpeedIncrease);
117 float deconstructTime = 0.0f;
120 deconstructTime += targetItem.
Prefab.
DeconstructTime / (deconstructionSpeed * deconstructionSpeedModifier);
123 progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
124 if (progressTimer > deconstructTime)
127 foreach (
Item targetItem
in items)
131 it.IsValidDeconstructor(
item) &&
132 (it.RequiredOtherItem.Length == 0 || it.RequiredOtherItem.Any(r => items.Any(it => it != targetItem && (it.HasTag(r) || it.Prefab.Identifier == r))))).ToList();
134 ProcessItem(targetItem, items, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.
Prefab.
DeconstructItems.Any());
137 item.CreateServerEvent(
this);
139 progressTimer = 0.0f;
140 progressState = 0.0f;
147 if (targetItem ==
null) {
return; }
150 float deconstructTime = validDeconstructItems.Any() ? targetItem.Prefab.DeconstructTime / (deconstructionSpeed * deconstructionSpeedModifier) : 1.0f;
152 progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
153 if (progressTimer > deconstructTime)
155 ProcessItem(targetItem, inputContainer.
Inventory.
AllItemsMod, validDeconstructItems, allowRemove: validDeconstructItems.Any() || !targetItem.Prefab.DeconstructItems.Any());
158 item.CreateServerEvent(
this);
160 progressTimer = 0.0f;
161 progressState = 0.0f;
167 private void ProcessItem(
Item targetItem, IEnumerable<Item> inputItems, List<DeconstructItem> validDeconstructItems,
bool allowRemove =
true)
172 float amountMultiplier = 1f;
174 if (user !=
null && !user.
Removed)
176 var abilityTargetItem =
new AbilityDeconstructedItem(targetItem, user);
184 var itemCreationMultiplier =
new AbilityItemCreationMultiplier(targetItem.
Prefab, amountMultiplier);
186 amountMultiplier = (int)itemCreationMultiplier.Value;
192 List<int> deconstructItemIndexes =
new List<int>();
193 for (
int i = 0; i < validDeconstructItems.Count; i++)
195 deconstructItemIndexes.Add(i);
197 List<float> commonness = validDeconstructItems.Select(i => i.Commonness).ToList();
198 List<DeconstructItem> products =
new List<DeconstructItem>();
200 for (
int i = 0; i < amount; i++)
202 if (deconstructItemIndexes.Count < 1) {
break; }
203 var itemIndex = ToolBox.SelectWeightedRandom(deconstructItemIndexes, commonness, Rand.RandSync.Unsynced);
204 products.Add(validDeconstructItems[itemIndex]);
205 var removeIndex = deconstructItemIndexes.IndexOf(itemIndex);
206 deconstructItemIndexes.RemoveAt(removeIndex);
207 commonness.RemoveAt(removeIndex);
210 foreach (DeconstructItem deconstructProduct
in products)
212 CreateDeconstructProduct(deconstructProduct, inputItems, (
int)(amountMultiplier * deconstructProduct.Amount));
217 foreach (DeconstructItem deconstructProduct
in validDeconstructItems)
219 CreateDeconstructProduct(deconstructProduct, inputItems, (
int)(amountMultiplier * deconstructProduct.Amount));
223 void CreateDeconstructProduct(DeconstructItem deconstructProduct, IEnumerable<Item> inputItems,
int amount)
227 if (percentageHealth < deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) {
return; }
229 if (MapEntityPrefab.FindByIdentifier(deconstructProduct.ItemIdentifier) is not ItemPrefab itemPrefab)
231 DebugConsole.ThrowError(
"Tried to deconstruct item \"" + targetItem.
Name +
"\" but couldn't find item prefab \"" + deconstructProduct.ItemIdentifier +
"\"!");
235 float condition = deconstructProduct.CopyCondition ?
236 percentageHealth * itemPrefab.Health * deconstructProduct.OutConditionMax :
237 itemPrefab.Health * Rand.Range(deconstructProduct.OutConditionMin, deconstructProduct.OutConditionMax);
241 foreach (
Item otherItem
in inputItems)
243 if (targetItem == otherItem) {
continue; }
244 if (deconstructProduct.RequiredOtherItem.Any(r => otherItem.HasTag(r) || r == otherItem.Prefab.Identifier))
246 var geneticMaterial1 = targetItem.GetComponent<GeneticMaterial>();
247 var geneticMaterial2 = otherItem.GetComponent<GeneticMaterial>();
248 if (geneticMaterial1 !=
null && geneticMaterial2 !=
null)
250 var result = geneticMaterial1.
Combine(geneticMaterial2, user);
251 if (result == GeneticMaterial.CombineResult.Refined)
255 Entity.Spawner.AddItemToRemoveQueue(otherItem);
257 if (result != GeneticMaterial.CombineResult.None)
259 OnCombinedOrRefined();
268 Entity.Spawner.AddItemToRemoveQueue(otherItem);
269 OnCombinedOrRefined();
274 void OnCombinedOrRefined()
277 foreach (Character character
in Character.GetFriendlyCrew(user))
279 character.CheckTalents(
AbilityEffectType.OnCrewGeneticMaterialCombinedOrRefined);
284 if (user !=
null && !user.
Removed)
287 var itemDeconstructedInventory =
new AbilityItemDeconstructedInventory(targetItem.
Prefab,
item);
291 for (
int i = 0; i < amount; i++)
293 Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.
Inventory, condition, onSpawned: (
Item spawnedItem) =>
295 spawnedItem.StolenDuringRound = targetItem.StolenDuringRound;
296 spawnedItem.AllowStealing = targetItem.AllowStealing;
297 spawnedItem.OriginalOutpost = targetItem.OriginalOutpost;
298 spawnedItem.SpawnedInCurrentOutpost = targetItem.SpawnedInCurrentOutpost;
299 if (RelocateOutputToMainSub && user is { AIController: HumanAIController humanAi })
301 humanAi.HandleRelocation(spawnedItem);
303 for (
int i = 0; i < outputContainer.
Capacity; i++)
306 bool combined =
false;
307 if (containedItem?.OwnInventory !=
null)
311 if (subItem.Combine(spawnedItem,
null))
320 if (containedItem?.
Combine(spawnedItem,
null) ??
false)
326 PutItemsToLinkedContainer();
333 Rand.Range(0.0f, 1.0f) < 0.05f)
335 GameAnalyticsManager.AddDesignEvent(
"ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ??
"none") +
":" + targetItem.
Prefab.
Identifier);
338 bool? result = GameMain.LuaCs.Hook.Call<
bool?>(
"item.deconstructed", targetItem,
this, user, allowRemove);
339 if (result ==
true) {
return; }
341 if (targetItem.AllowDeconstruct && allowRemove)
344 foreach (ItemContainer ic
in targetItem.GetComponents<ItemContainer>())
346 if (ic?.Inventory ==
null || ic.RemoveContainedItemsOnDeconstruct) {
continue; }
347 foreach (
Item outputItem
in ic.Inventory.AllItemsMod)
349 tryPutInOutputSlots(outputItem);
352 humanAi.HandleRelocation(outputItem);
357 Entity.Spawner.AddItemToRemoveQueue(targetItem);
359 PutItemsToLinkedContainer();
363 if (Entity.Spawner?.IsInRemoveQueue(targetItem) ??
false)
365 targetItem.Drop(dropper:
null);
369 tryPutInOutputSlots(targetItem);
375 for (
int i = 0; i < outputContainer.
Capacity; i++)
378 if (containedItem?.OwnInventory !=
null && containedItem.GetComponent<GeneticMaterial>() ==
null && containedItem.OwnInventory.TryPutItem(
item, user:
null))
390 private void PutItemsToLinkedContainer()
392 if (GameMain.NetworkMember !=
null && GameMain.NetworkMember.IsClient) {
return; }
395 foreach (MapEntity linkedTo
in item.linkedTo)
397 if (linkedTo is Item linkedItem)
399 var fabricator = linkedItem.GetComponent<Fabricator>();
400 if (fabricator !=
null) {
continue; }
401 var itemContainer = linkedItem.GetComponent<ItemContainer>();
402 if (itemContainer ==
null) {
continue; }
403 outputContainer.
Inventory.
AllItemsMod.ForEach(containedItem => itemContainer.Inventory.TryPutItem(containedItem, user:
null, createNetworkEvent:
true));
411 private void MoveInputQueue()
417 if (!inputContainer.
Inventory.
TryPutItem(item1, i + 1, allowSwapping:
false, allowCombine:
false, user:
null, createNetworkEvent:
true))
425 private IEnumerable<(
Item item, DeconstructItem output)> GetAvailableOutputs(
bool checkRequiredOtherItems =
true)
428 foreach (Item inputItem
in items)
430 if (!inputItem.AllowDeconstruct) {
continue; }
431 foreach (var deconstructItem
in inputItem.Prefab.DeconstructItems)
433 if (deconstructItem.RequiredDeconstructor.Length > 0)
435 if (!deconstructItem.RequiredDeconstructor.Any(r => item.HasTag(r) || item.Prefab.Identifier == r)) {
continue; }
437 if (deconstructItem.RequiredOtherItem.Length > 0 && checkRequiredOtherItems)
439 if (!deconstructItem.RequiredOtherItem.Any(r => items.Any(it => it.HasTag(r) || it.Prefab.Identifier == r))) {
continue; }
440 bool validOtherItemFound =
false;
441 foreach (Item otherInputItem
in items)
443 if (otherInputItem == inputItem) {
continue; }
444 if (!deconstructItem.RequiredOtherItem.Any(r => otherInputItem.HasTag(r) || otherInputItem.Prefab.Identifier == r)) {
continue; }
446 var geneticMaterial1 = inputItem.GetComponent<GeneticMaterial>();
447 var geneticMaterial2 = otherInputItem.GetComponent<GeneticMaterial>();
448 if (geneticMaterial1 !=
null && geneticMaterial2 !=
null)
450 if (!geneticMaterial1.CanBeCombinedWith(geneticMaterial2)) {
continue; }
452 validOtherItemFound =
true;
454 if (!validOtherItemFound) {
continue; }
456 yield
return (inputItem, deconstructItem);
463 PutItemsToLinkedContainer();
466 RelocateOutputToMainSub =
false;
472 userDeconstructorSpeedMultiplier = user !=
null ? 1f + user.GetStatValue(
StatTypes.DeconstructorSpeedMultiplier) : 1f;
477 GameServer.Log(GameServer.CharacterLogName(user) + (IsActive ?
" activated " :
" deactivated ") + item.Name,
ServerLog.
MessageType.ItemInteraction);
479 if (createNetworkEvent)
481 item.CreateServerEvent(
this);
486 progressTimer = 0.0f;
487 progressState = 0.0f;
492 HintManager.OnStartDeconstructing(user,
this);
493 if (
Item.
Submarine is { Info.IsOutpost: true } && user is { IsBot: true })
495 HintManager.OnItemMarkedForRelocation();
519 Value = itemAmountMultiplier;
522 public float Value {
get;
set; }
static IEnumerable< Character > GetFriendlyCrew(Character character)
void CheckTalents(AbilityEffectType abilityEffectType, AbilityObject abilityObject)
static EntitySpawner Spawner
bool IsInRemoveQueue(Entity entity)
static NetworkMember NetworkMember
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
IEnumerable< Item > AllItemsMod
All items contained in the inventory. Allows modifying the contents of the inventory while being enum...
Item GetItemAt(int index)
Get the item stored in the specified inventory slot. If the slot contains a stack of items,...
Item LastOrDefault()
Return the last item in the inventory, or null if the inventory is empty.
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
ItemStatManager StatManager
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
IEnumerable< Item > ContainedItems
bool Combine(Item item, Character user)
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
override bool CanBePutInSlot(Item item, int i, bool ignoreCondition=false)
Can the item be put in the specified slot.
override void RemoveItem(Item item)
bool RandomDeconstructionOutput
ImmutableArray< DeconstructItem > DeconstructItems
int RandomDeconstructionOutputAmount
AbilityDeconstructedItem(Item item, Character character)
AbilityItemCreationMultiplier(ItemPrefab itemPrefab, float itemAmountMultiplier)
AbilityItemDeconstructedInventory(ItemPrefab itemPrefab, Item item)
void SetActive(bool active, Character user=null, bool createNetworkEvent=false)
Deconstructor(Item item, ContentXElement element)
ItemContainer OutputContainer
float DeconstructionSpeed
override void Update(float deltaTime, Camera cam)
bool DeconstructItemsSimultaneously
ItemContainer InputContainer
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
bool RelocateOutputToMainSub
Should the output items left in the deconstructor be automatically moved to the main sub at the end o...
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
virtual bool Combine(Item item, Character user)
readonly ItemInventory Inventory
readonly NamedEvent< ItemContainer > OnContainedItemsChanged
const float MaxOverVoltageFactor
Maximum voltage factor when the device is being overvolted. I.e. how many times more effectively the ...
float powerConsumption
The maximum amount of power the item can draw from connected items
ContentPackage? ContentPackage
readonly Identifier Identifier
Interface for entities that the clients can send events to the server
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.