3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Globalization;
16 private ImmutableDictionary<uint, FabricationRecipe> fabricationRecipes;
18 private const int MaxAmountToFabricate = 99;
21 private float timeUntilReady;
22 private float requiredTime;
24 private string savedFabricatedItem;
25 private float savedTimeUntilReady, savedRequiredTime;
27 private readonly Dictionary<Identifier, List<Item>> availableIngredients =
new Dictionary<Identifier, List<Item>>();
29 const float RefreshIngredientsInterval = 1.0f;
30 private float refreshIngredientsTimer;
32 private bool hasPower;
44 private int amountToFabricate;
48 get {
return amountToFabricate; }
49 set { amountToFabricate = MathHelper.Clamp(value, 1, MaxAmountToFabricate); }
52 private int amountRemaining;
54 private const float TinkeringSpeedIncrease = 2.5f;
56 private enum FabricatorState
63 private FabricatorState state;
64 private FabricatorState State
72 if (state == value) {
return; }
76 item.CreateServerEvent(
this);
83 get {
return inputContainer; }
88 get {
return outputContainer; }
91 private float progressState;
93 private readonly Dictionary<uint, int> fabricationLimits =
new Dictionary<uint, int>();
100 foreach (var subElement
in element.Elements())
102 if (subElement.Name.ToString().Equals(
"fabricableitem", StringComparison.OrdinalIgnoreCase))
104 DebugConsole.ThrowError(
"Error in item " +
item.
Name +
"! Fabrication recipes should be defined in the craftable item's xml, not in the fabricator.",
105 contentPackage: element.ContentPackage);
110 var fabricationRecipes =
new Dictionary<uint, FabricationRecipe>();
126 bool recipeInvalid =
false;
129 if (requiredItem.ItemPrefabs.None())
131 DebugConsole.ThrowError($
"Error in the fabrication recipe for \"{itemPrefab.Name}\". Could not find the ingredient \"{requiredItem}\".",
132 contentPackage: packageToLog);
133 recipeInvalid =
true;
136 if (recipeInvalid) {
continue; }
138 if (fabricationRecipes.TryGetValue(recipe.
RecipeHash, out var duplicateRecipe))
140 DebugConsole.ThrowError($
"Error in the fabrication recipe for \"{itemPrefab.Name}\". Duplicate recipe in \"{duplicateRecipe.TargetItem.Identifier}\".",
141 contentPackage: packageToLog);
144 fabricationRecipes.Add(recipe.
RecipeHash, recipe);
145 if (recipe.FabricationLimitMax >= 0)
151 this.fabricationRecipes = fabricationRecipes.ToImmutableDictionary();
153 state = FabricatorState.Stopped;
160 if (containers.Count < 2)
162 DebugConsole.ThrowError(
"Error in item \"" +
item.
Name +
"\": Fabricators must have two ItemContainer components!");
166 inputContainer = containers[0];
167 outputContainer = containers[1];
169 foreach (var recipe
in fabricationRecipes.Values)
171 if (recipe.RequiredItems.Length > inputContainer.
Capacity)
173 DebugConsole.ThrowErrorLocalized(
"Error in item \"" +
item.
Name +
"\": There's not enough room in the input inventory for the ingredients of \"" + recipe.TargetItem.Name +
"\"!");
177 OnItemLoadedProjSpecific();
180 partial
void OnItemLoadedProjSpecific();
184 SelectProjSpecific(character);
185 return base.Select(character);
188 partial
void SelectProjSpecific(
Character character);
192 return picker !=
null;
197 fabricationRecipes = fabricationRecipes
198 .Where(kvp => allowedIdentifiers.Contains(kvp.Value.TargetItemPrefabIdentifier))
199 .ToImmutableDictionary();
204 partial
void CreateRecipes();
208 if (selectedItem ==
null) {
return; }
213 fabricatedItem = selectedItem;
214 RefreshAvailableIngredients();
218 if (amountInput !=
null)
222 RefreshActivateButtonText();
225 bool isClient = GameMain.NetworkMember !=
null && GameMain.NetworkMember.IsClient;
228 MoveIngredientsToInputContainer(selectedItem);
231 requiredTime = GetRequiredTime(fabricatedItem, user);
232 timeUntilReady = requiredTime;
237 if (GameMain.NetworkMember?.IsServer ??
true)
239 State = FabricatorState.Active;
242 if (user !=
null && addToServerLog && selectedItem.
RequiredMoney == 0)
246 GameServer.Log($
"{GameServer.CharacterLogName(user)} bought {selectedItem.DisplayName.Value} for {selectedItem.RequiredMoney} mk from {item.Name}",
ServerLog.
MessageType.Money);
250 GameServer.Log($
"{GameServer.CharacterLogName(user)} started fabricating {selectedItem.DisplayName.Value} in {item.Name}",
ServerLog.
MessageType.ItemInteraction);
256 private void CancelFabricating(Character user =
null)
262 progressState = 0.0f;
263 timeUntilReady = 0.0f;
264 UpdateRequiredTimeProjSpecific();
268 if (GameMain.NetworkMember?.IsServer ??
true)
270 State = FabricatorState.Stopped;
273 if (fabricatedItem ==
null) {
return; }
281 if (amountInput !=
null)
285 RefreshActivateButtonText();
287 fabricatedItem =
null;
292 if (refreshIngredientsTimer <= 0.0f)
294 RefreshAvailableIngredients();
295 refreshIngredientsTimer = RefreshIngredientsInterval;
297 refreshIngredientsTimer -= deltaTime;
303 if (fabricatedItem ==
null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
310 progressState = fabricatedItem ==
null ? 0.0f : (requiredTime - timeUntilReady) / requiredTime;
314 hasPower = State != FabricatorState.Paused;
326 State = FabricatorState.Paused;
329 State = FabricatorState.Active;
332 float tinkeringStrength = 0f;
334 if (repairable !=
null)
337 if (repairable.IsTinkering)
339 tinkeringStrength = repairable.TinkeringStrength;
346 float fabricationSpeedIncrease = 1f + tinkeringStrength * TinkeringSpeedIncrease;
350 UpdateRequiredTimeProjSpecific();
352 if (timeUntilReady <= 0.0f)
358 private Client GetUsingClient()
361 return GameMain.Server.ConnectedClients.Find(c => c.Character == user);
367 private void Fabricate()
369 RefreshAvailableIngredients();
370 if (fabricatedItem ==
null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
378 if (user ==
null) {
return; }
379 if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
382 if (GetUsingClient() is { } client)
384 mpCampaign.TryPurchase(client, fabricatedItem.
RequiredMoney);
392 else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
398 bool ingredientsStolen =
false;
399 bool ingredientsAllowStealing =
true;
401 if (GameMain.NetworkMember is
null || GameMain.NetworkMember.IsServer)
403 List<Item> chosenIngredients =
new List<Item>();
404 var suitableIngredients = GetSortedSuitableIngredients();
408 for (
int i = 0; i < requiredItem.Amount; i++)
410 foreach (var suitableIngredient
in suitableIngredients)
412 if (!requiredItem.MatchesItem(suitableIngredient)) {
continue; }
413 if (chosenIngredients.Contains(suitableIngredient)) {
continue; }
415 ingredientsStolen |= suitableIngredient.StolenDuringRound;
416 if (!suitableIngredient.AllowStealing)
418 ingredientsAllowStealing =
false;
422 if (requiredItem.UseCondition && suitableIngredient.ConditionPercentage - requiredItem.MinCondition * 100 > 0.0f)
424 suitableIngredient.Condition -= suitableIngredient.Prefab.Health * requiredItem.MinCondition;
427 if (suitableIngredient.OwnInventory !=
null)
431 if (suitableIngredient.GetComponent<ItemContainer>()?.RemoveContainedItemsOnDeconstruct ??
false)
433 Entity.Spawner.AddItemToRemoveQueue(containedItem);
437 containedItem.Drop(dropper:
null);
441 chosenIngredients.Add(suitableIngredient);
447 var fabricationIngredients =
new AbilityFabricationItemIngredients(chosenIngredients);
450 foreach (
Item availableItem
in fabricationIngredients.Items)
452 Entity.Spawner.AddItemToRemoveQueue(availableItem);
458 var fabricationitemAmount =
new AbilityFabricationItemAmount(fabricatedItem.
TargetItem, fabricatedItem.
Amount);
461 if (fabricatedItem.
Quality.HasValue)
463 quality = fabricatedItem.
Quality.Value;
465 else if (user?.Info !=
null)
467 foreach (Character character
in Character.GetFriendlyCrew(user))
469 character.CheckTalents(
AbilityEffectType.OnAllyItemFabricatedAmount, fabricationitemAmount);
474 GetFabricatedItemQuality(fabricatedItem, user).Quality :
475 GetFabricatedItemQuality(fabricatedItem, user).RollQuality();
478 int amount = (int)fabricationitemAmount.Value;
479 if (fabricationLimits.ContainsKey(fabricatedItem.
RecipeHash))
481 if (amount > fabricationLimits[fabricatedItem.
RecipeHash])
483 amount = fabricationLimits[fabricatedItem.
RecipeHash];
484 fabricationLimits[fabricatedItem.
RecipeHash] = 0;
488 fabricationLimits[fabricatedItem.
RecipeHash] -= amount;
493 for (
int i = 0; i < amount; i++)
498 Rand.Range(0.0f, 1.0f) < 0.05f)
500 GameAnalyticsManager.AddDesignEvent(
"ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ??
"none") +
":" + fabricatedItem.
TargetItem.
Identifier);
502 if (i < amountFittingContainer)
505 onSpawned: (
Item spawnedItem) =>
507 onItemSpawned(spawnedItem, tempUser);
508 spawnedItem.Quality = quality;
509 spawnedItem.StolenDuringRound = ingredientsStolen;
510 spawnedItem.AllowStealing = ingredientsAllowStealing;
512 spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
518 onSpawned: (
Item spawnedItem) =>
520 onItemSpawned(spawnedItem, tempUser);
521 spawnedItem.Quality = quality;
522 spawnedItem.StolenDuringRound = ingredientsStolen;
523 spawnedItem.AllowStealing = ingredientsAllowStealing;
525 spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
530 void onItemSpawned(
Item spawnedItem, Character user)
534 foreach (WifiComponent wifiComponent
in spawnedItem.GetComponents<WifiComponent>())
536 wifiComponent.TeamID = user.TeamID;
541 if (user?.Info !=
null && !user.Removed)
545 float addedSkill = skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill;
546 var addedSkillValue =
new AbilityFabricatorSkillGain(skill.Identifier, addedSkill);
547 user.CheckTalents(
AbilityEffectType.OnItemFabricationSkillGain, addedSkillValue);
548 user.Info.ApplySkillGain(
550 addedSkillValue.Value);
554 var prevFabricatedItem = fabricatedItem;
559 if (amountRemaining > 0 && CanBeFabricated(prevFabricatedItem, availableIngredients, prevUser))
562 StartFabricating(prevFabricatedItem, prevUser, addToServerLog:
false);
586 => Math.Clamp((skillLevel - target) / (100f - target) * 100f, min: 0, max: 100);
588 public readonly record
struct QualityResult(int
Quality, bool HasRandomQuality, float PlusOnePercentage, float PlusTwoPercentage)
592 public bool HasRandomQualityRollChance => HasRandomQuality && (PlusOnePercentage > 0f || PlusTwoPercentage > 0f);
596 public float TotalPlusOnePercentage => Math.Clamp(PlusOnePercentage * (100f - PlusTwoPercentage) / 100f, min: 0, max: 100);
597 public float TotalPlusTwoPercentage => Math.Clamp(PlusOnePercentage * PlusTwoPercentage / 100f, min: 0, max: 100);
599 public int RollQuality()
601 int additionalQuality = 0;
602 if (Roll(PlusOnePercentage))
605 if (Roll(PlusTwoPercentage))
611 return Quality + additionalQuality;
613 static bool Roll(
float percentage)
614 => percentage >= Rand.Range(0, 100, Rand.RandSync.Unsynced);
619 PlusTwoQualityBonusThreshold = 75;
632 float floatQuality = 0.0f;
636 floatQuality += user.Info.GetSavedStatValue(
StatTypes.IncreaseFabricationQuality, tag);
642 quality = (int)floatQuality;
645 Option<float> plusOne = Option.None,
646 plusTwo = Option.None;
650 float skillLevel = user.GetSkillLevel(skill.Identifier);
659 plusOne = OverrideChanceIfLess(plusOne, bonusChance1);
661 if (skillLevel >= PlusTwoQualityBonusThreshold)
664 plusTwo = OverrideChanceIfLess(plusTwo, bonusChance2);
676 static Option<float> OverrideChanceIfLess(Option<float> original,
float bonusChance)
678 if (original.TryUnwrap(out var originalChance))
680 return originalChance > bonusChance ? Option.Some(bonusChance) : original;
683 return Option.Some(bonusChance);
688 float PlusOnePercentage = plusOne.Match(some:
static f => f, none:
static () => 0f);
689 float PlusTwoPercentage = plusTwo.Match(some:
static f => f, none:
static () => 0f);
691 if (!hasRandomQuality && PlusOnePercentage > 0)
694 if (PlusTwoPercentage > 0)
706 partial
void UpdateRequiredTimeProjSpecific();
721 private readonly HashSet<Item> usedIngredients =
new HashSet<Item>();
723 private bool CanBeFabricated(
FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients,
Character character)
725 if (fabricableItem ==
null) {
return false; }
728 if (character ==
null) {
return false; }
729 if (!AnyOneHasRecipeForItem(character, fabricableItem.
TargetItem))
737 if (character is not { IsTraitor:
true }) {
return false; }
742 switch (GameMain.GameSession?.GameMode)
744 case MultiPlayerCampaign mpCampaign:
746 if (!mpCampaign.CanAfford(fabricableItem.
RequiredMoney, GetUsingClient())) {
return false; }
750 case CampaignMode campaign:
752 if (campaign.Bank.Balance < fabricableItem.
RequiredMoney) {
return false; }
761 if (fabricationLimits.TryGetValue(fabricableItem.
RecipeHash, out
int amount) && amount <= 0)
767 usedIngredients.Clear();
771 int availableItemsAmount = 0;
772 foreach (ItemPrefab requiredPrefab
in requiredItem.ItemPrefabs)
774 if (!availableIngredients.TryGetValue(requiredPrefab.Identifier, out var availableItems)) { continue; }
776 foreach (
Item availableItem in availableItems)
778 if (usedIngredients.Contains(availableItem)) { continue; }
779 if (requiredItem.IsConditionSuitable(availableItem.ConditionPercentage))
781 usedIngredients.Add(availableItem);
782 availableItemsAmount++;
785 if (availableItemsAmount >= requiredItem.Amount)
796 private float GetRequiredTime(FabricationRecipe fabricableItem, Character user)
798 float degreeOfSuccess = FabricationDegreeOfSuccess(user, fabricableItem.RequiredSkills);
800 float t = degreeOfSuccess < 0.5f ? degreeOfSuccess * degreeOfSuccess : degreeOfSuccess * 2;
804 float time = fabricableItem.RequiredTime / item.StatManager.GetAdjustedValueMultiplicative(ItemTalentStats.FabricationSpeed, FabricationSpeed) / MathHelper.Clamp(t, 0.01f, 2.0f);
806 if (user?.Info is { } info && fabricableItem.TargetItem is { } it)
808 time /= 1f + it.
Tags.Sum(tag => info.GetSavedStatValue(
StatTypes.FabricationSpeed, tag));
815 if (skills.Length == 0) {
return 1.0f; }
816 if (character ==
null) {
return 0.0f; }
818 float minDegreeOfSuccess = 1.0f;
819 foreach (var skill
in skills)
821 float characterLevel = character.
GetSkillLevel(skill.Identifier);
822 minDegreeOfSuccess = Math.Min(minDegreeOfSuccess, (characterLevel - (skill.Level * SkillRequirementMultiplier) + 100.0f) / 2.0f / 100.0f);
824 return minDegreeOfSuccess;
829 return SkillRequirementMultiplier;
833 private readonly HashSet<Inventory> linkedInventories =
new HashSet<Inventory>();
835 private void RefreshAvailableIngredients()
841 linkedInventories.Clear();
842 List<Item> itemList =
new List<Item>();
844 foreach (
MapEntity linkedTo
in item.linkedTo)
846 if (linkedTo is
Item linkedItem)
848 var itemContainer = linkedItem.GetComponent<
ItemContainer>();
849 if (itemContainer ==
null) {
continue; }
852 if (!itemContainer.HasRequiredItems(user, addMessage:
false)) {
continue; }
855 var deconstructor = linkedItem.GetComponent<Deconstructor>();
856 if (deconstructor !=
null)
858 itemContainer = deconstructor.OutputContainer;
861 linkedInventories.Add(itemContainer.Inventory);
862 itemList.AddRange(itemContainer.Inventory.AllItems);
865 for (
int i = 0; i < itemList.Count; i++)
867 var container = itemList[i].GetComponent<ItemContainer>();
868 if (container !=
null)
870 itemList.AddRange(container.Inventory.AllItems);
873 if (user?.Inventory !=
null && user.
SelectedItem == item)
878 foreach (Character c
in Character.CharacterList)
882 if (c.SelectedItem !=
null &&
883 c.Inventory !=
null &&
884 linkedInventories.Contains(c.SelectedItem.OwnInventory) &&
885 !linkedInventories.Contains(c.Inventory))
887 itemList.AddRange(c.Inventory.AllItems);
888 linkedInventories.Add(c.Inventory);
891 availableIngredients.Clear();
892 foreach (Item item
in itemList)
894 var itemIdentifier = item.Prefab.Identifier;
895 if (!availableIngredients.ContainsKey(itemIdentifier))
897 availableIngredients[itemIdentifier] =
new List<Item>(itemList.Count);
899 availableIngredients[itemIdentifier].Add(item);
901 foreach (var itemId
in availableIngredients.Keys)
903 availableIngredients[itemId] = SortIngredients(availableIngredients[itemId]).ToList();
907 private IEnumerable<Item> SortIngredients(IEnumerable<Item> items)
910 .OrderByDescending(getIngredientContainerPriority)
911 .ThenBy(it => it.Prefab.DefaultPrice?.Price ?? 0)
912 .ThenBy(it => MathUtils.IsValid(it.Condition) ? it.Condition : 0)
913 .ThenByDescending(it => it.ParentInventory?.FindIndex(it) ?? 0);
915 int getIngredientContainerPriority(Item item)
917 if (item.ParentInventory == InputContainer.Inventory)
921 else if (item.ParentInventory is CharacterInventory)
929 private IEnumerable<Item> GetSortedSuitableIngredients()
931 List<Item> suitableIngredients =
new List<Item>();
932 foreach (FabricationRecipe.RequiredItem requiredItem in fabricatedItem.
RequiredItems)
934 foreach (ItemPrefab requiredPrefab
in requiredItem.ItemPrefabs)
936 if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) {
continue; }
937 var availableItems = availableIngredients[requiredPrefab.Identifier];
938 suitableIngredients.AddRange(
939 availableItems.Where(potentialItem => requiredItem.IsConditionSuitable(potentialItem.ConditionPercentage)));
943 return SortIngredients(suitableIngredients);
950 private void MoveIngredientsToInputContainer(FabricationRecipe targetItem)
952 List<Item> chosenIngredients =
new List<Item>();
953 var suitableIngredients = GetSortedSuitableIngredients();
955 foreach (var requiredItem
in targetItem.RequiredItems)
957 for (
int i = 0; i < requiredItem.Amount; i++)
959 foreach (var suitableIngredient
in suitableIngredients)
961 if (!requiredItem.MatchesItem(suitableIngredient)) {
continue; }
962 if (chosenIngredients.Contains(suitableIngredient)) {
continue; }
965 if (suitableIngredient.ParentInventory != inputContainer.
Inventory)
969 var unneededItem = inputContainer.
Inventory.
AllItems.FirstOrDefault(it => !chosenIngredients.Contains(it));
970 unneededItem?.Drop(
null);
974 chosenIngredients.Add(suitableIngredient);
980 RefreshAvailableIngredients();
983 public override XElement
Save(XElement parentElement)
985 var componentElement = base.Save(parentElement);
986 if (fabricatedItem !=
null)
988 componentElement.Add(
new XAttribute(
"fabricateditemidentifier", fabricatedItem.
TargetItem.
Identifier));
989 componentElement.Add(
new XAttribute(
"savedtimeuntilready", timeUntilReady.ToString(
"G", CultureInfo.InvariantCulture)));
990 componentElement.Add(
new XAttribute(
"savedrequiredtime", requiredTime.ToString(
"G", CultureInfo.InvariantCulture)));
992 return componentElement;
997 base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
998 savedFabricatedItem = componentElement.
GetAttributeString(
"fabricateditemidentifier",
"");
999 savedTimeUntilReady = componentElement.
GetAttributeFloat(
"savedtimeuntilready", 0.0f);
1000 savedRequiredTime = componentElement.
GetAttributeFloat(
"savedrequiredtime", 0.0f);
1005 if (
string.IsNullOrEmpty(savedFabricatedItem)) {
return; }
1010 var recipe = fabricationRecipes.Values.FirstOrDefault(r => r.TargetItem.Identifier == savedFabricatedItem);
1013 DebugConsole.ThrowError(
"Error while loading a fabricator. Can't continue fabricating \"" + savedFabricatedItem +
"\" (matching recipe not found).");
1018 SelectItem(
null, recipe, savedRequiredTime);
1020 StartFabricating(recipe, user:
null);
1021 timeUntilReady = savedTimeUntilReady;
1022 requiredTime = savedRequiredTime;
1024 savedFabricatedItem =
null;
1029 base.RemoveComponentSpecific();
1030 OnItemFabricated =
null;
1035 public AbilityFabricatorSkillGain(Identifier skillIdentifier,
float skillAmount)
1037 SkillIdentifier = skillIdentifier;
1038 Value = skillAmount;
1040 public float Value {
get;
set; }
1041 public Identifier SkillIdentifier {
get;
set; }
1046 public AbilityFabricationItemAmount(ItemPrefab itemPrefab,
float itemAmount)
1048 ItemPrefab = itemPrefab;
1051 public float Value {
get;
set; }
1052 public ItemPrefab ItemPrefab {
get;
set; }
1055 internal sealed
class AbilityFabricationItemIngredients :
AbilityObject
1057 public List<Item> Items {
get;
set; }
1059 public AbilityFabricationItemIngredients(List<Item> items)
float GetSkillLevel(string skillIdentifier)
void CheckTalents(AbilityEffectType abilityEffectType, AbilityObject abilityObject)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
float GetStatValue(StatTypes statType, bool includeSaved=true)
bool HasRecipeForItem(Identifier recipeIdentifier)
CharacterInventory Inventory
static Character? Controlled
string? GetAttributeString(string key, string? def)
float GetAttributeFloat(string key, float def)
ContentXElement? GetChildElement(string name)
readonly ImmutableArray< Identifier > SuitableFabricatorIdentifiers
readonly float OutCondition
readonly int RequiredMoney
LocalizedString DisplayName
readonly ImmutableArray< RequiredItem > RequiredItems
readonly ImmutableArray< Skill > RequiredSkills
readonly bool RequiresRecipe
readonly int FabricationLimitMin
How many of this item the fabricator can create (< 0 = unlimited)
readonly bool HideForNonTraitors
static GameSession?? GameSession
static NetworkMember NetworkMember
static ImmutableHashSet< Character > GetSessionCrewCharacters(CharacterType type)
Returns a list of crew characters currently in the game with a given filter.
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...
bool CanBePut(Item item)
Can the item be put in the inventory (i.e. is there a suitable free slot or a stack the item can be p...
ItemInventory OwnInventory
override Vector2? Position
bool HasTag(Identifier tag)
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
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 int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot=false)
override void RemoveItem(Item item)
static readonly PrefabCollection< ItemPrefab > Prefabs
ImmutableDictionary< uint, FabricationRecipe > FabricationRecipes
ContentPackage GetParentModPackageOrThisPackage()
If the base prefab this one is a variant of is defined in a non-vanilla package, returns that non-van...
override ImmutableHashSet< Identifier > Tags
ContentXElement ConfigElement
void RemoveFabricationRecipes(IEnumerable< Identifier > allowedIdentifiers)
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
override void RemoveComponentSpecific()
ItemContainer OutputContainer
override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
override bool Select(Character character)
override float GetCurrentPowerConsumption(Connection connection=null)
Power consumption of the fabricator. Only consume power when active and adjust consumption based on c...
Action< Item, Character > OnItemFabricated
static float CalculateBonusRollPercentage(float skillLevel, float target)
Fabricator(Item item, ContentXElement element)
override XElement Save(XElement parentElement)
float SkillRequirementMultiplier
override bool Pick(Character picker)
a Character has picked the item
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
float FabricationDegreeOfSuccess(Character character, ImmutableArray< Skill > skills)
override void Update(float deltaTime, Camera cam)
readonly record struct QualityResult(int Quality, bool HasRandomQuality, float PlusOnePercentage, float PlusTwoPercentage)
ItemContainer InputContainer
override float GetSkillMultiplier()
const int PlusOneQualityBonusThreshold
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)
readonly ItemInventory Inventory
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
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
float currPowerConsumption
The amount of power currently consumed by the item. Negative values mean that the item is providing p...
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.