Unstable 0.1400.0.0

This commit is contained in:
Markus Isberg
2021-05-11 15:47:47 +03:00
parent 3f324b14e8
commit 92f0264af2
247 changed files with 8238 additions and 1911 deletions

View File

@@ -15,6 +15,7 @@ using Barotrauma.Extensions;
using Barotrauma.Steam;
using System.Threading.Tasks;
using Barotrauma.MapCreatures.Behavior;
using static Barotrauma.FabricationRecipe;
namespace Barotrauma
{
@@ -1367,6 +1368,237 @@ namespace Barotrauma
}
}, isCheat: false));
commands.Add(new Command("analyzeitem", "analyzeitem: Analyzes one item for exploits.", (string[] args) =>
{
if (args.Length < 1) return;
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
foreach (ItemPrefab iPrefab in ItemPrefab.Prefabs)
{
fabricableItems.AddRange(iPrefab.FabricationRecipes);
}
string itemNameOrId = args[0].ToLowerInvariant();
ItemPrefab itemPrefab =
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
if (itemPrefab == null)
{
NewMessage("Item not found for analyzing.");
return;
}
NewMessage("Analyzing item " + itemPrefab.Name + " with base cost " + itemPrefab.DefaultPrice.Price);
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
// omega nesting incoming
if (fabricationRecipe != null)
{
foreach (Tuple<string, PriceInfo> itemLocationPrice in itemPrefab.GetSellPricesOver(0))
{
NewMessage(" If bought at " + itemLocationPrice.Item1 + " it costs " + itemLocationPrice.Item2.Price);
int totalPrice = 0;
int? totalBestPrice = 0;
foreach (var ingredient in fabricationRecipe.RequiredItems)
{
foreach (ItemPrefab ingredientItemPrefab in ingredient.ItemPrefabs)
{
NewMessage(" Its ingredient " + ingredientItemPrefab.Name + " has base cost " + ingredientItemPrefab.DefaultPrice.Price);
totalPrice += ingredientItemPrefab.DefaultPrice.Price;
totalBestPrice += ingredientItemPrefab.GetMinPrice();
int basePrice = ingredientItemPrefab.DefaultPrice.Price;
foreach (Tuple<string, PriceInfo> ingredientItemLocationPrice in ingredientItemPrefab.GetBuyPricesUnder())
{
if (basePrice > ingredientItemLocationPrice.Item2.Price)
{
NewMessage(" Location " + ingredientItemLocationPrice.Item1 + " sells ingredient " + ingredientItemPrefab.Name + " for cheaper, " + ingredientItemLocationPrice.Item2.Price, Color.Yellow);
}
else
{
NewMessage(" Location " + ingredientItemLocationPrice.Item1 + " sells ingredient " + ingredientItemPrefab.Name + " for more, " + ingredientItemLocationPrice.Item2.Price, Color.Teal);
}
}
}
}
int costDifference = itemPrefab.DefaultPrice.Price - totalPrice;
NewMessage(" Constructing the item from store-bought items provides " + costDifference + " profit with default values.");
if (totalBestPrice.HasValue)
{
int? bestDifference = itemLocationPrice.Item2.Price - totalBestPrice;
NewMessage(" Constructing the item from store-bought items provides " + bestDifference + " profit with best-case scenario values.");
}
}
}
}, isCheat: false));
commands.Add(new Command("checkcraftingexploits", "checkcraftingexploits: Finds outright item exploits created by buying store-bought ingredients and constructing them into sellable items.", (string[] args) =>
{
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
{
fabricableItems.AddRange(itemPrefab.FabricationRecipes);
}
List<Tuple<string, int>> costDifferences = new List<Tuple<string, int>>();
int maximumAllowedCost = 5;
if (args.Length > 0)
{
Int32.TryParse(args[0], out maximumAllowedCost);
}
foreach (ItemPrefab itemPrefab in ItemPrefab.Prefabs)
{
int? defaultCost = itemPrefab.DefaultPrice?.Price;
int? fabricationCostStore = null;
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == itemPrefab);
if (fabricationRecipe == null)
{
continue;
}
bool canBeBought = true;
foreach (var ingredient in fabricationRecipe.RequiredItems)
{
int? ingredientPrice = ingredient.ItemPrefabs.Where(p => p.CanBeBought).Min(ip => ip.DefaultPrice?.Price);
if (ingredientPrice.HasValue)
{
if (!fabricationCostStore.HasValue) { fabricationCostStore = 0; }
float useAmount = ingredient.UseCondition ? ingredient.MinCondition : 1.0f;
fabricationCostStore += (int)(ingredientPrice.Value * ingredient.Amount * useAmount);
}
else
{
canBeBought = false;
}
}
if (fabricationCostStore.HasValue && defaultCost.HasValue && canBeBought)
{
int costDifference = defaultCost.Value - fabricationCostStore.Value;
if (costDifference > maximumAllowedCost || costDifference < 0f)
{
float ratio = (float)fabricationCostStore.Value / defaultCost.Value;
string message = "Fabricating \"" + itemPrefab.Name + "\" costs " + (int)(ratio * 100) + "% of the price of the item, or " + costDifference + " more. Item price: " + defaultCost.Value + ", ingredient prices: " + fabricationCostStore.Value;
costDifferences.Add(new Tuple<string, int>(message, costDifference));
}
}
}
costDifferences.Sort((x, y) => x.Item2.CompareTo(y.Item2));
foreach (Tuple<string, int> costDifference in costDifferences)
{
Color color = Color.Yellow;
NewMessage(costDifference.Item1, color);
}
}, isCheat: false));
commands.Add(new Command("adjustprice", "adjustprice: Recursively prints out expected price adjustments for items derived from this item.", (string[] args) =>
{
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
foreach (ItemPrefab iP in ItemPrefab.Prefabs)
{
fabricableItems.AddRange(iP.FabricationRecipes);
}
if (args.Length < 2)
{
NewMessage("Item or value not defined.");
return;
}
string itemNameOrId = args[0].ToLowerInvariant();
ItemPrefab materialPrefab =
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
if (materialPrefab == null)
{
NewMessage("Item not found for price adjustment.");
return;
}
AdjustItemTypes adjustItemType = AdjustItemTypes.NoAdjustment;
if (args.Length > 2)
{
switch (args[2].ToLowerInvariant())
{
case "add":
adjustItemType = AdjustItemTypes.Additive;
break;
case "mult":
adjustItemType = AdjustItemTypes.Multiplicative;
break;
}
}
if (Int32.TryParse(args[1].ToLowerInvariant(), out int newPrice))
{
Dictionary<ItemPrefab, int> newPrices = new Dictionary<ItemPrefab, int>();
PrintItemCosts(newPrices, materialPrefab, fabricableItems, newPrice, true, adjustItemType: adjustItemType);
PrintItemCosts(newPrices, materialPrefab, fabricableItems, newPrice, false, adjustItemType: adjustItemType);
}
}, isCheat: false));
commands.Add(new Command("deconstructvalue", "deconstructvalue: Views and compares deconstructed component prices for this item.", (string[] args) =>
{
List<FabricationRecipe> fabricableItems = new List<FabricationRecipe>();
foreach (ItemPrefab iP in ItemPrefab.Prefabs)
{
fabricableItems.AddRange(iP.FabricationRecipes);
}
if (args.Length < 1)
{
NewMessage("Item not defined.");
return;
}
string itemNameOrId = args[0].ToLowerInvariant();
ItemPrefab parentItem =
(MapEntityPrefab.Find(itemNameOrId, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
if (parentItem == null)
{
NewMessage("Item not found for price adjustment.");
return;
}
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == parentItem);
int totalValue = 0;
NewMessage(parentItem.Name + " has the price " + parentItem.DefaultPrice.Price);
if (fabricationRecipe != null)
{
NewMessage(" It constructs from:");
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
{
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
{
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price);
totalValue += itemPrefab.DefaultPrice.Price;
}
}
NewMessage("Its total value was: " + totalValue);
totalValue = 0;
}
NewMessage(" The item deconstructs into:");
foreach (DeconstructItem deconstructItem in parentItem.DeconstructItems)
{
ItemPrefab itemPrefab =
(MapEntityPrefab.Find(deconstructItem.ItemIdentifier, identifier: null, showErrorMessages: false) ??
MapEntityPrefab.Find(null, identifier: itemNameOrId, showErrorMessages: false)) as ItemPrefab;
NewMessage(" " + itemPrefab.Name + " has the price " + itemPrefab.DefaultPrice.Price);
totalValue += itemPrefab.DefaultPrice.Price;
}
NewMessage("Its deconstruct value was: " + totalValue);
}, isCheat: false));
commands.Add(new Command("setentityproperties", "setentityproperties [property name] [value]: Sets the value of some property on all selected items/structures in the sub editor.", (string[] args) =>
{
if (args.Length != 2 || Screen.Selected != GameMain.SubEditorScreen) { return; }
@@ -2935,5 +3167,155 @@ namespace Barotrauma
return false;
}
}
private enum AdjustItemTypes
{
NoAdjustment,
Additive,
Multiplicative
}
private static void PrintItemCosts(Dictionary<ItemPrefab, int> newPrices, ItemPrefab materialPrefab, List<FabricationRecipe> fabricableItems, int newPrice, bool adjustDown, string depth = "", AdjustItemTypes adjustItemType = AdjustItemTypes.NoAdjustment)
{
if (newPrice < 1)
{
NewMessage(depth + materialPrefab.Name + " cannot be adjusted to this price, because it would become less than 1.");
return;
}
depth += " ";
if (newPrice > 0)
{
newPrices.TryAdd(materialPrefab, newPrice);
}
int componentCost = 0;
int newComponentCost = 0;
var fabricationRecipe = fabricableItems.Find(f => f.TargetItem == materialPrefab);
if (fabricationRecipe != null)
{
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
{
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
{
GetAdjustedPrice(itemPrefab, ref componentCost, ref newComponentCost, newPrices);
}
}
}
string componentCostMultiplier = "";
if (componentCost > 0)
{
componentCostMultiplier = $" (Relative difference to component cost {GetComponentCostDifference(materialPrefab.DefaultPrice.Price, componentCost)} => {GetComponentCostDifference(newPrice, newComponentCost)}, or flat profit {(int)(materialPrefab.DefaultPrice.Price - (int)componentCost)} => {newPrice - newComponentCost})";
}
string priceAdjustment = "";
if (newPrice != materialPrefab.DefaultPrice.Price)
{
priceAdjustment = ", Suggested price adjustment is " + materialPrefab.DefaultPrice.Price + " => " + newPrice;
}
NewMessage(depth + materialPrefab.Name + "(" + materialPrefab.DefaultPrice.Price + ") " + priceAdjustment + componentCostMultiplier);
if (adjustDown)
{
if (componentCost > 0)
{
double newPriceMult = (double)newPrice / (double)(materialPrefab.DefaultPrice.Price);
int newPriceDiff = componentCost + newPrice - materialPrefab.DefaultPrice.Price;
switch (adjustItemType)
{
case AdjustItemTypes.Additive:
NewMessage(depth + materialPrefab.Name + "'s components should be adjusted " + componentCost + " => " + newPriceDiff);
break;
case AdjustItemTypes.Multiplicative:
NewMessage(depth + materialPrefab.Name + "'s components should be adjusted " + componentCost + " => " + Math.Round(newPriceMult * componentCost));
break;
}
if (fabricationRecipe != null)
{
foreach (RequiredItem requiredItem in fabricationRecipe.RequiredItems)
{
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
{
if (itemPrefab.DefaultPrice != null)
{
switch (adjustItemType)
{
case AdjustItemTypes.NoAdjustment:
PrintItemCosts(newPrices, itemPrefab, fabricableItems, itemPrefab.DefaultPrice.Price, adjustDown, depth, adjustItemType);
break;
case AdjustItemTypes.Additive:
PrintItemCosts(newPrices, itemPrefab, fabricableItems, itemPrefab.DefaultPrice.Price + (int)((newPrice - materialPrefab.DefaultPrice.Price) / (double)fabricationRecipe.RequiredItems.Count), adjustDown, depth, adjustItemType);
break;
case AdjustItemTypes.Multiplicative:
PrintItemCosts(newPrices, itemPrefab, fabricableItems, (int)(itemPrefab.DefaultPrice.Price * newPriceMult), adjustDown, depth, adjustItemType);
break;
}
}
}
}
}
}
}
else
{
var fabricationRecipes = fabricableItems.Where(f => f.RequiredItems.Any(x => x.ItemPrefabs.Contains(materialPrefab)));
foreach (FabricationRecipe fabricationRecipeParent in fabricationRecipes)
{
if (fabricationRecipeParent.TargetItem.DefaultPrice != null)
{
int targetComponentCost = 0;
int newTargetComponentCost = 0;
foreach (RequiredItem requiredItem in fabricationRecipeParent.RequiredItems)
{
foreach (ItemPrefab itemPrefab in requiredItem.ItemPrefabs)
{
GetAdjustedPrice(itemPrefab, ref targetComponentCost, ref newTargetComponentCost, newPrices);
}
}
switch (adjustItemType)
{
case AdjustItemTypes.NoAdjustment:
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, fabricationRecipeParent.TargetItem.DefaultPrice.Price, adjustDown, depth, adjustItemType);
break;
case AdjustItemTypes.Additive:
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, fabricationRecipeParent.TargetItem.DefaultPrice.Price + newPrice - materialPrefab.DefaultPrice.Price, adjustDown, depth, adjustItemType);
break;
case AdjustItemTypes.Multiplicative:
double maintainedMultiplier = GetComponentCostDifference(fabricationRecipeParent.TargetItem.DefaultPrice.Price, targetComponentCost);
PrintItemCosts(newPrices, fabricationRecipeParent.TargetItem, fabricableItems, (int)(newTargetComponentCost * maintainedMultiplier), adjustDown, depth, adjustItemType);
break;
}
}
}
}
}
private static double GetComponentCostDifference(int itemCost, int componentCost)
{
return Math.Round((double)(itemCost / (double)componentCost), 2);
}
private static void GetAdjustedPrice(ItemPrefab itemPrefab, ref int componentCost, ref int newComponentCost, Dictionary<ItemPrefab, int> newPrices)
{
if (newPrices.TryGetValue(itemPrefab, out int newPrice))
{
newComponentCost += newPrice;
}
else if (itemPrefab.DefaultPrice != null)
{
newComponentCost += itemPrefab.DefaultPrice.Price;
}
if (itemPrefab.DefaultPrice != null)
{
componentCost += itemPrefab.DefaultPrice.Price;
}
}
}
}