357 lines
15 KiB
C#
357 lines
15 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Xml.Linq;
|
|
using Barotrauma.Networking;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
|
|
namespace Barotrauma.Items.Components
|
|
{
|
|
internal class VineSprite
|
|
{
|
|
[Serialize("0,0,0,0", IsPropertySaveable.No)]
|
|
public Rectangle SourceRect { get; private set; }
|
|
|
|
[Serialize("0.5,0.5", IsPropertySaveable.No)]
|
|
public Vector2 Origin { get; private set; }
|
|
|
|
public Vector2 AbsoluteOrigin;
|
|
|
|
public VineSprite(ContentXElement element)
|
|
{
|
|
SerializableProperty.DeserializeProperties(this, element);
|
|
AbsoluteOrigin = new Vector2(SourceRect.Width * Origin.X, SourceRect.Height * Origin.Y);
|
|
}
|
|
}
|
|
|
|
internal partial class Growable
|
|
{
|
|
public readonly Dictionary<VineTileType, VineSprite> VineSprites = new Dictionary<VineTileType, VineSprite>();
|
|
public readonly List<Sprite> FlowerSprites = new List<Sprite>();
|
|
public readonly List<Sprite> LeafSprites = new List<Sprite>();
|
|
|
|
public Sprite? VineAtlas, DecayAtlas;
|
|
|
|
protected override void RemoveComponentSpecific()
|
|
{
|
|
VineAtlas?.Remove();
|
|
DecayAtlas?.Remove();
|
|
|
|
foreach (Sprite sprite in FlowerSprites)
|
|
{
|
|
sprite.Remove();
|
|
}
|
|
|
|
foreach (Sprite sprite in LeafSprites)
|
|
{
|
|
sprite.Remove();
|
|
}
|
|
}
|
|
|
|
public void Draw(SpriteBatch spriteBatch, Planter planter, Vector2 offset, float depth)
|
|
{
|
|
const float zStep = 0.0001f;
|
|
float leafDepth = 0f;
|
|
|
|
foreach (VineTile vine in Vines)
|
|
{
|
|
leafDepth += zStep;
|
|
DrawBranch(vine, spriteBatch, planter.Item.DrawPosition + offset, depth, leafDepth);
|
|
}
|
|
|
|
if (GameMain.DebugDraw)
|
|
{
|
|
foreach (Rectangle rect in FailedRectangles)
|
|
{
|
|
Rectangle wRect = rect;
|
|
wRect.Y = -wRect.Y;
|
|
wRect.Y -= wRect.Height;
|
|
GUI.DrawRectangle(spriteBatch, wRect, Color.Red);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawBranch(VineTile vine, SpriteBatch spriteBatch, Vector2 position, float depth, float leafDepth)
|
|
{
|
|
Vector2 pos = position + vine.Position;
|
|
pos.Y = -pos.Y;
|
|
|
|
VineSprite vineSprite = VineSprites[vine.Type];
|
|
Color color = Decayed ? DeadTint : VineTint;
|
|
|
|
float layer1 = depth + 0.01f, // flowers
|
|
layer2 = depth + 0.02f, // decay atlas
|
|
layer3 = depth + 0.03f; // branches and leaves
|
|
|
|
float scale = VineScale * vine.VineStep;
|
|
|
|
if (VineAtlas != null && VineAtlas.Loaded)
|
|
{
|
|
spriteBatch.Draw(VineAtlas.Texture, pos + vine.offset, vineSprite.SourceRect, color, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer3);
|
|
}
|
|
|
|
if (DecayAtlas != null && DecayAtlas.Loaded)
|
|
{
|
|
spriteBatch.Draw(DecayAtlas.Texture, pos, vineSprite.SourceRect, vine.HealthColor, 0f, vineSprite.AbsoluteOrigin, scale, SpriteEffects.None, layer2);
|
|
}
|
|
|
|
if (vine.FlowerConfig.Variant >= 0 && !Decayed)
|
|
{
|
|
Sprite flowerSprite = FlowerSprites[vine.FlowerConfig.Variant];
|
|
flowerSprite.Draw(spriteBatch, pos, FlowerTint, flowerSprite.Origin, scale: BaseFlowerScale * vine.FlowerConfig.Scale * vine.FlowerStep, rotate: vine.FlowerConfig.Rotation, depth: layer1);
|
|
}
|
|
|
|
if (vine.LeafConfig.Variant >= 0)
|
|
{
|
|
Sprite leafSprite = LeafSprites[vine.LeafConfig.Variant];
|
|
leafSprite.Draw(spriteBatch, pos, Decayed ? DeadTint : LeafTint, leafSprite.Origin, scale: BaseLeafScale * vine.LeafConfig.Scale * vine.FlowerStep, rotate: vine.LeafConfig.Rotation, depth: layer3 + leafDepth);
|
|
}
|
|
}
|
|
|
|
partial void LoadVines(ContentXElement element)
|
|
{
|
|
ContentPath vineAtlasPath = element.GetAttributeContentPath("vineatlas") ?? ContentPath.Empty;
|
|
ContentPath decayAtlasPath = element.GetAttributeContentPath("decayatlas") ?? ContentPath.Empty;
|
|
|
|
if (!vineAtlasPath.IsNullOrEmpty())
|
|
{
|
|
VineAtlas = new Sprite(vineAtlasPath.Value, Rectangle.Empty);
|
|
}
|
|
|
|
if (!decayAtlasPath.IsNullOrEmpty())
|
|
{
|
|
DecayAtlas = new Sprite(decayAtlasPath.Value, Rectangle.Empty);
|
|
}
|
|
|
|
foreach (var subElement in element.Elements())
|
|
{
|
|
switch (subElement.Name.ToString().ToLowerInvariant())
|
|
{
|
|
case "vinesprite":
|
|
VineTileType type = subElement.GetAttributeEnum("type", VineTileType.Stem);
|
|
VineSprites.Add(type, new VineSprite(subElement));
|
|
break;
|
|
case "flowersprite":
|
|
FlowerSprites.Add(new Sprite(subElement));
|
|
break;
|
|
case "leafsprite":
|
|
LeafSprites.Add(new Sprite(subElement));
|
|
break;
|
|
}
|
|
|
|
flowerVariants = FlowerSprites.Count;
|
|
leafVariants = LeafSprites.Count;
|
|
}
|
|
|
|
foreach (VineTileType type in Enum.GetValues(typeof(VineTileType)).Cast<VineTileType>())
|
|
{
|
|
if (!VineSprites.ContainsKey(type))
|
|
{
|
|
DebugConsole.ThrowError($"Vine sprite missing from {item.Prefab.Identifier}: {type}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly object mutex = new object();
|
|
|
|
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
|
{
|
|
Health = msg.ReadRangedSingle(0, MaxHealth, 8);
|
|
int startOffset = msg.ReadRangedInteger(-1, MaximumVines);
|
|
if (startOffset > -1)
|
|
{
|
|
int vineCount = msg.ReadRangedInteger(0, VineChunkSize);
|
|
List<VineTile> tiles = new List<VineTile>();
|
|
for (int i = 0; i < vineCount; i++)
|
|
{
|
|
VineTileType vineType = (VineTileType)msg.ReadRangedInteger(0b0000, 0b1111);
|
|
int flowerConfig = msg.ReadRangedInteger(0, 0xFFF);
|
|
int leafConfig = msg.ReadRangedInteger(0, 0xFFF);
|
|
sbyte posX = (sbyte)msg.ReadByte(), posY = (sbyte)msg.ReadByte();
|
|
Vector2 pos = new Vector2(posX * VineTile.Size, posY * VineTile.Size);
|
|
|
|
tiles.Add(new VineTile(this, pos, vineType, FoliageConfig.Deserialize(flowerConfig), FoliageConfig.Deserialize(leafConfig)));
|
|
}
|
|
|
|
// is this even needed??
|
|
lock (mutex)
|
|
{
|
|
for (var i = 0; i < vineCount; i++)
|
|
{
|
|
int index = i + startOffset;
|
|
if (index >= Vines.Count)
|
|
{
|
|
Vines.Add(tiles[i]);
|
|
continue;
|
|
}
|
|
|
|
VineTile oldVine = Vines[index];
|
|
VineTile newVine = tiles[i];
|
|
newVine.GrowthStep = oldVine.GrowthStep;
|
|
Vines[index] = newVine;
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateBranchHealth();
|
|
ResetPlanterSize();
|
|
}
|
|
|
|
private void ResetPlanterSize()
|
|
{
|
|
if (item.ParentInventory is ItemInventory itemInventory && itemInventory.Owner is Item parentItem)
|
|
{
|
|
if (parentItem.GetComponent<Planter>() is { } planter)
|
|
{
|
|
planter.Item.ResetCachedVisibleSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
private int seed;
|
|
|
|
// Huge bowl of spaghetti
|
|
public void CreateDebugHUD(Planter planter, PlantSlot slot)
|
|
{
|
|
Vector2 relativeSize = new Vector2(0.3f, 0.6f);
|
|
GUIMessageBox msgBox = new GUIMessageBox(item.Name, "", new[] { TextManager.Get("applysettingsbutton") }, relativeSize);
|
|
|
|
GUILayoutGroup content = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.85f), msgBox.Content.RectTransform)) { Stretch = true };
|
|
GUINumberInput seedInput = CreateIntEntry("Random Seed", seed, content.RectTransform);
|
|
GUINumberInput vineTileSizeInput = CreateIntEntry("Vine Tile Size (Global)", VineTile.Size, content.RectTransform);
|
|
GUINumberInput[] leafScaleRangeInput = CreateMinMaxEntry("Leaf Scale Range (Global)", new []{ MinLeafScale, MaxLeafScale }, 1.5f, content.RectTransform);
|
|
GUINumberInput[] flowerScaleRangeInput = CreateMinMaxEntry("Flower Scale Range (Global)", new []{ MinFlowerScale, MaxFlowerScale }, 1.5f, content.RectTransform);
|
|
GUINumberInput vineCountInput = CreateIntEntry("Vine Count", MaximumVines, content.RectTransform);
|
|
GUINumberInput vineScaleInput = CreateFloatEntry("Vine Scale", VineScale, content.RectTransform);
|
|
GUINumberInput flowerInput = CreateIntEntry("Flower Quantity", FlowerQuantity, content.RectTransform);
|
|
GUINumberInput flowerScaleInput = CreateFloatEntry("Flower Scale", BaseFlowerScale, content.RectTransform);
|
|
GUINumberInput leafScaleInput = CreateFloatEntry("Leaf Scale", BaseLeafScale, content.RectTransform);
|
|
GUINumberInput leafProbabilityInput = CreateFloatEntry("Leaf Probability", LeafProbability, content.RectTransform);
|
|
GUINumberInput[] leafTintInputs = CreateMinMaxEntry("Leaf Tint", new []{ LeafTint.R / 255f, LeafTint.G / 255f, LeafTint.B / 255f }, 1.0f, content.RectTransform);
|
|
GUINumberInput[] flowerTintInputs = CreateMinMaxEntry("Flower Tint", new []{ FlowerTint.R / 255f, FlowerTint.G / 255f, FlowerTint.B / 255f }, 1.0f, content.RectTransform);
|
|
GUINumberInput[] vineTintInputs = CreateMinMaxEntry("Branch Tint", new []{ VineTint.R / 255f, VineTint.G / 255f, VineTint.B / 255f }, 1.0f, content.RectTransform);
|
|
|
|
// Apply
|
|
msgBox.Buttons[0].OnClicked = (button, o) =>
|
|
{
|
|
seed = seedInput.IntValue;
|
|
MaximumVines = vineCountInput.IntValue;
|
|
FlowerQuantity = flowerInput.IntValue;
|
|
BaseFlowerScale = flowerScaleInput.FloatValue;
|
|
VineScale = vineScaleInput.FloatValue;
|
|
BaseLeafScale = leafScaleInput.FloatValue;
|
|
LeafProbability = leafProbabilityInput.FloatValue;
|
|
VineTile.Size = vineTileSizeInput.IntValue;
|
|
|
|
MinFlowerScale = flowerScaleRangeInput[0].FloatValue;
|
|
MaxFlowerScale = flowerScaleRangeInput[1].FloatValue;
|
|
MinLeafScale = leafScaleRangeInput[0].FloatValue;
|
|
MaxLeafScale = leafScaleRangeInput[1].FloatValue;
|
|
|
|
LeafTint = new Color(leafTintInputs[0].FloatValue, leafTintInputs[1].FloatValue, leafTintInputs[2].FloatValue);
|
|
FlowerTint = new Color(flowerTintInputs[0].FloatValue, flowerTintInputs[1].FloatValue, flowerTintInputs[2].FloatValue);
|
|
VineTint = new Color(vineTintInputs[0].FloatValue, vineTintInputs[1].FloatValue, vineTintInputs[2].FloatValue);
|
|
|
|
if (FlowerQuantity >= MaximumVines - 1)
|
|
{
|
|
vineCountInput.Flash(Color.Red);
|
|
flowerInput.Flash(Color.Red);
|
|
return false;
|
|
}
|
|
|
|
if (MinFlowerScale > MaxFlowerScale)
|
|
{
|
|
foreach (GUINumberInput input in flowerScaleRangeInput)
|
|
{
|
|
input.Flash(Color.Red);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (MinLeafScale > MaxLeafScale)
|
|
{
|
|
foreach (GUINumberInput input in leafScaleRangeInput)
|
|
{
|
|
input.Flash(Color.Red);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
msgBox.Close();
|
|
|
|
Random random = new Random(seed);
|
|
Random flowerRandom = new Random(seed);
|
|
Vines.Clear();
|
|
GenerateFlowerTiles(flowerRandom);
|
|
GenerateStem();
|
|
|
|
Decayed = false;
|
|
FullyGrown = false;
|
|
while (MaximumVines > Vines.Count)
|
|
{
|
|
if (!CanGrowMore())
|
|
{
|
|
Decayed = true;
|
|
break;
|
|
}
|
|
|
|
TryGenerateBranches(planter, slot, random, flowerRandom);
|
|
}
|
|
|
|
if (!Decayed)
|
|
{
|
|
FullyGrown = true;
|
|
}
|
|
|
|
foreach (VineTile vineTile in Vines)
|
|
{
|
|
vineTile.GrowthStep = 2.0f;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
}
|
|
|
|
private static GUINumberInput CreateIntEntry(string label, int defaultValue, RectTransform parent)
|
|
{
|
|
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
|
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
|
|
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), NumberType.Int) { IntValue = defaultValue };
|
|
return input;
|
|
}
|
|
|
|
private static GUINumberInput CreateFloatEntry(string label, float defaultValue, RectTransform parent)
|
|
{
|
|
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
|
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
|
|
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), NumberType.Float) { FloatValue = defaultValue, DecimalsToDisplay = 2 };
|
|
return input;
|
|
}
|
|
|
|
private static GUINumberInput[] CreateMinMaxEntry(string label, float[] values, float max, RectTransform parent, float min = 0f)
|
|
{
|
|
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.08f), parent), isHorizontal: true);
|
|
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), layout.RectTransform), label);
|
|
GUINumberInput[] inputs = new GUINumberInput[values.Length];
|
|
for (var i = 0; i < values.Length; i++)
|
|
{
|
|
float value = values[i];
|
|
GUINumberInput input = new GUINumberInput(new RectTransform(new Vector2(0.5f / values.Length, 1f), layout.RectTransform), NumberType.Float)
|
|
{
|
|
FloatValue = value, DecimalsToDisplay = 2,
|
|
MinValueFloat = min,
|
|
MaxValueFloat = max
|
|
};
|
|
inputs[i] = input;
|
|
}
|
|
|
|
return inputs;
|
|
}
|
|
#endif
|
|
}
|
|
} |