Trade Menu

Implemented Trade Menu
Item Filtering by size, hardpoint, behavior
Buying from trade menu on double click
This commit is contained in:
Reinier Vens 2021-01-14 23:47:08 +00:00
commit f4ee26e96c
22 changed files with 631 additions and 73 deletions

View file

@ -51,12 +51,14 @@ public class ActionGameManager : MonoBehaviour
private AetheriaInput _input;
private int _zoomLevelIndex;
private Ship _currentShip;
// private ShipInput _shipInput;
private float2 _shipYawPitch;
private float3 _viewDirection;
private (HardpointData[] hardpoints, Transform[] barrels, PlaceUIElementWorldspace crosshair)[] ArticulationGroups;
private (TargetLock targetLock, PlaceUIElementWorldspace indicator, Rotate spin)[] LockingIndicators;
public List<Ship> PlayerShips { get; } = new List<Ship>();
public EquippedDockingBay DockingBay { get; private set; }
public Ship CurrentShip
{
@ -531,6 +533,7 @@ public class ActionGameManager : MonoBehaviour
var bay = entity.TryDock(_currentShip);
if (bay != null)
{
DockingBay = bay;
DockCamera.enabled = true;
FollowCamera.enabled = false;
_currentShip.Active = false;
@ -576,6 +579,7 @@ public class ActionGameManager : MonoBehaviour
else if (_currentShip.Parent.TryUndock(_currentShip))
{
Menu.gameObject.SetActive(false);
DockingBay = null;
_currentShip.Active = true;
//_shipInput = new ShipInput(_input.Player, _currentShip);
DockCamera.enabled = false;

View file

@ -26,6 +26,6 @@ public class GuidedProjectileManager : MonoBehaviour
p.LiftCurve = data.LiftCurve.ToCurve();
p.ThrustCurve = data.ThrustCurve.ToCurve();
p.Thrust = source.Entity.ItemManager.Evaluate(data.Thrust, item.EquippableItem, source.Entity);
p.TopSpeed = source.Entity.ItemManager.Evaluate(data.MissileSpeed, item.EquippableItem, source.Entity);
p.TopSpeed = source.Entity.ItemManager.Evaluate(data.Velocity, item.EquippableItem, source.Entity);
}
}

View file

@ -11,7 +11,7 @@ using Newtonsoft.Json;
public class EnergyDrawData : BehaviorData
{
[InspectableField, JsonProperty("draw"), Key(1), RuntimeInspectable]
public PerformanceStat Draw = new PerformanceStat();
public PerformanceStat EnergyDraw = new PerformanceStat();
[InspectableField, JsonProperty("perSecond"), Key(2)]
public bool PerSecond;
@ -42,7 +42,7 @@ public class EnergyDraw : IBehavior
public bool Execute(float delta)
{
Entity.Energy -= Context.Evaluate(_data.Draw, Item.EquippableItem, Entity) * (_data.PerSecond ? delta : 1);
Entity.Energy -= Context.Evaluate(_data.EnergyDraw, Item.EquippableItem, Entity) * (_data.PerSecond ? delta : 1);
return true;
}
}

View file

@ -27,8 +27,8 @@ public class LauncherData : InstantWeaponData
[InspectableField, JsonProperty("launchSpeed"), Key(14)]
public PerformanceStat LaunchSpeed = new PerformanceStat();
[InspectableField, JsonProperty("missileSpeed"), Key(15)]
public PerformanceStat MissileSpeed = new PerformanceStat();
[InspectableField, JsonProperty("missileSpeed"), Key(15), RuntimeInspectable]
public PerformanceStat Velocity = new PerformanceStat();
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
{

View file

@ -9,13 +9,13 @@ using Newtonsoft.Json;
[InspectableField, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
public class ProjectileWeaponData : InstantWeaponData
{
[InspectableField, JsonProperty("spread"), Key(9)]
[InspectableField, JsonProperty("spread"), Key(9), RuntimeInspectable]
public PerformanceStat Spread = new PerformanceStat();
[InspectableField, JsonProperty("bulletInherit"), Key(10)]
public float Inherit;
[InspectableField, JsonProperty("bulletVelocity"), Key(11)]
[InspectableField, JsonProperty("bulletVelocity"), Key(11), RuntimeInspectable]
public PerformanceStat Velocity = new PerformanceStat();
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)

View file

@ -12,18 +12,18 @@ using static Unity.Mathematics.math;
public class TargetLockData : BehaviorData
{
[InspectableField, JsonProperty("speed"), Key(1), RuntimeInspectable]
public PerformanceStat Speed = new PerformanceStat();
public PerformanceStat LockSpeed = new PerformanceStat();
[InspectableField, JsonProperty("sensorImpact"), Key(2), RuntimeInspectable]
[InspectableField, JsonProperty("sensorImpact"), Key(2)]
public PerformanceStat SensorImpact = new PerformanceStat();
[InspectableField, JsonProperty("threshold"), Key(3), RuntimeInspectable]
public PerformanceStat Threshold = new PerformanceStat();
public PerformanceStat LockAngle = new PerformanceStat();
[InspectableField, JsonProperty("directionImpact"), Key(4), RuntimeInspectable]
[InspectableField, JsonProperty("directionImpact"), Key(4)]
public PerformanceStat DirectionImpact = new PerformanceStat();
[InspectableField, JsonProperty("decay"), Key(5), RuntimeInspectable]
[InspectableField, JsonProperty("decay"), Key(5)]
public PerformanceStat Decay = new PerformanceStat();
public override IBehavior CreateInstance(ItemManager context, Entity entity, EquippedItem item)
@ -75,9 +75,9 @@ public class TargetLock : IBehavior, IAlwaysUpdatedBehavior
}
if (Entity.Target.Value != null)
{
Speed = Context.Evaluate(_data.Speed, Item.EquippableItem, Entity);
Speed = Context.Evaluate(_data.LockSpeed, Item.EquippableItem, Entity);
SensorImpact = Context.Evaluate(_data.SensorImpact, Item.EquippableItem, Entity);
Threshold = Context.Evaluate(_data.Threshold, Item.EquippableItem, Entity);
Threshold = Context.Evaluate(_data.LockAngle, Item.EquippableItem, Entity);
DirectionImpact = Context.Evaluate(_data.DirectionImpact, Item.EquippableItem, Entity);
Decay = Context.Evaluate(_data.Decay, Item.EquippableItem, Entity);

View file

@ -13,10 +13,10 @@ public class ThrusterData : BehaviorData
[InspectableField, JsonProperty("thrust"), Key(1), RuntimeInspectable]
public PerformanceStat Thrust = new PerformanceStat();
[InspectableField, JsonProperty("visibility"), Key(2)]
[InspectableField, JsonProperty("visibility"), Key(2), RuntimeInspectable]
public PerformanceStat Visibility = new PerformanceStat();
[InspectableField, JsonProperty("heat"), Key(3)]
[InspectableField, JsonProperty("heat"), Key(3), RuntimeInspectable]
public PerformanceStat Heat = new PerformanceStat();
[InspectablePrefab, JsonProperty("Particles"), Key(4)]

View file

@ -8,32 +8,32 @@ using Newtonsoft.Json;
[Union(0, typeof(ProjectileWeaponData)),
Union(1, typeof(LauncherData)),
Union(2, typeof(ConstantWeaponData))]
Union(2, typeof(ConstantWeaponData)), RuntimeInspectable]
public abstract class WeaponData : BehaviorData
{
[InspectableField, JsonProperty("damageType"), Key(1)]
[InspectableField, JsonProperty("damageType"), Key(1), RuntimeInspectable]
public DamageType DamageType;
[InspectableField, JsonProperty("damage"), Key(2)]
[InspectableField, JsonProperty("damage"), Key(2), RuntimeInspectable]
public PerformanceStat Damage = new PerformanceStat();
[RangedFloatInspectable(0,1), JsonProperty("penetration"), Key(3)]
[RangedFloatInspectable(0,1), JsonProperty("penetration"), Key(3), RuntimeInspectable]
public PerformanceStat Penetration = new PerformanceStat();
[RangedFloatInspectable(0,1), JsonProperty("damageSpread"), Key(4)]
public PerformanceStat DamageSpread = new PerformanceStat();
[InspectableField, JsonProperty("range"), Key(5)]
[InspectableField, JsonProperty("range"), Key(5), RuntimeInspectable]
public PerformanceStat Range = new PerformanceStat();
[InspectablePrefab, JsonProperty("effect"), Key(6)]
public string EffectPrefab;
}
[InspectableField, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
[InspectableField, MessagePackObject, JsonObject(MemberSerialization.OptIn), RuntimeInspectable]
public abstract class InstantWeaponData : WeaponData
{
[InspectableField, JsonProperty("burstCount"), Key(7)]
[InspectableField, JsonProperty("burstCount"), Key(7), RuntimeInspectable]
public int BurstCount;
[InspectableField, JsonProperty("burstTime"), Key(8)]

View file

@ -109,14 +109,22 @@ public static class Extensions
);
}
public static string FormatTypeName(this string typeName)
{
return (typeName.EndsWith("Data")
? typeName.Substring(0, typeName.Length - 4)
: typeName).SplitCamelCase();
}
public static string SignificantDigits(this float d, int digits=10)
{
int magnitude = (d == 0.0f) ? 0 : (int)Math.Floor(Math.Log10(Math.Abs(d))) + 1;
int magnitude = d == 0.0f ? 0 : (int)Math.Floor(Math.Log10(Math.Abs(d))) + 1;
digits -= magnitude;
if (digits < 0)
digits = 0;
string fmt = "f" + digits.ToString();
return d.ToString(fmt);
string fmt = "f" + digits;
string strdec = d.ToString(fmt);
return strdec.Contains(".") ? strdec.TrimEnd('0').TrimEnd('.') : strdec;
}
private static Random? _random;

View file

@ -1,3 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
@ -9,9 +10,17 @@ public class SizeFilter : MonoBehaviour
public TMP_InputField Width;
public TMP_InputField Height;
public Button DisableButton;
public event Action OnDisable;
void Start()
{
DisableButton.onClick.AddListener(() => gameObject.SetActive(false));
DisableButton.onClick.AddListener(() =>
{
gameObject.SetActive(false);
OnDisable?.Invoke();
OnDisable = null;
});
//gameObject.SetActive(false);
}
}

View file

@ -2,52 +2,221 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class TradeMenu : MonoBehaviour
{
public ActionGameManager GameManager;
public DropdownMenu Dropdown;
public ContextMenu ContextMenu;
public Button NewFilterButton;
public Prototype FilterPrototype;
public SizeFilter MinimumSizeFilter;
public SizeFilter MaximumSizeFilter;
public RectTransform ItemList;
public PropertiesPanel Properties;
public Spreadsheet Spreadsheet;
public TextMeshProUGUI TargetCargoLabel;
public Button FoldoutButton;
private EquippedCargoBay _targetCargo;
private (ItemFilter filter, HardpointType type) _hardpointFilter;
private List<(ItemFilter filter, Type type)> _behaviorFilters = new List<(ItemFilter filter, Type type)>();
private void OnEnable()
{
_targetCargo = GameManager.DockingBay;
TargetCargoLabel.text = "Docking Bay";
Properties.Context = GameManager.ItemManager;
Spreadsheet.ShowData(
new[] {"Name", "Mass"},
new[] {2, 1},
GameManager.ItemManager.ItemData.GetAll<ItemData>().Select(i => new SpreadsheetEntryRow
{
Columns = new[]
MinimumSizeFilter.Width.onEndEdit.AddListener(_ => Populate());
MinimumSizeFilter.Height.onEndEdit.AddListener(_ => Populate());
MaximumSizeFilter.Width.onEndEdit.AddListener(_ => Populate());
MaximumSizeFilter.Height.onEndEdit.AddListener(_ => Populate());
NewFilterButton.onClick.AddListener(() =>
{
ContextMenu.Clear();
IEnumerable<HardpointType> hardpointTypes = (HardpointType[]) Enum.GetValues(typeof(HardpointType));
if (_hardpointFilter.filter != null)
hardpointTypes = hardpointTypes.Where(x => x != _hardpointFilter.type);
ContextMenu.AddDropdown("Item Type", hardpointTypes
.Select<HardpointType, (string, Action, bool)>(x => (Enum.GetName(typeof(HardpointType), x), () =>
{
new SpreadsheetEntryColumn
if (_hardpointFilter.filter == null)
{
Output = () => i.Name,
SortKey = i.Name
},
new SpreadsheetEntryColumn
_hardpointFilter.filter = FilterPrototype.Instantiate<ItemFilter>();
_hardpointFilter.filter.OnDisable += () =>
{
_hardpointFilter.filter = null;
Populate();
};
}
_hardpointFilter.filter.Label.text = $"Hardpoint: {Enum.GetName(typeof(HardpointType), x)}";
_hardpointFilter.type = x;
Populate();
}, true)));
ContextMenu.AddDropdown("Item Behavior", typeof(BehaviorData).GetAllChildClasses()
.Where(x=> x.GetCustomAttribute<RuntimeInspectable>() != null && _behaviorFilters.All(f => f.type != x))
.Select<Type, (string, Action, bool)>(x=> (x.Name.FormatTypeName(), () =>
{
var matchingType = _behaviorFilters.FirstOrDefault(y => y.type.IsAssignableFrom(x) || x.IsAssignableFrom(y.type));
if (matchingType.filter != null) matchingType.filter.DisableButton.onClick.Invoke();
var filter = FilterPrototype.Instantiate<ItemFilter>();
filter.Label.text = x.Name.FormatTypeName();
filter.OnDisable += () =>
{
Output = () => i.Mass.SignificantDigits(3),
SortKey = i.Mass
},
},
_behaviorFilters.Remove((filter, x));
Populate();
};
_behaviorFilters.Add((filter, x));
Populate();
}, true)));
if(!MinimumSizeFilter.gameObject.activeSelf)
ContextMenu.AddOption("Minimum Size",
() =>
{
MinimumSizeFilter.gameObject.SetActive(true);
MinimumSizeFilter.OnDisable += () => Populate();
});
if(!MaximumSizeFilter.gameObject.activeSelf)
ContextMenu.AddOption("Maximum Size",
() =>
{
MaximumSizeFilter.gameObject.SetActive(true);
MaximumSizeFilter.OnDisable += () => Populate();
});
ContextMenu.Show();
});
Populate();
}
void Populate()
{
var columns = new List<(string name, int size, Func<EquippableItemData, Func<string>> output, Func<EquippableItemData, IComparable> sortKey)>();
columns.Add(("Name", 3,
data => () => data.Name,
data => data.Name));
if(_hardpointFilter.filter==null)
columns.Add(("Type", 2,
data => () => Enum.GetName(typeof(HardpointType), data.HardpointType),
data => data.HardpointType));
columns.Add(("Mass", 1,
data => () => data.Mass.SignificantDigits(3),
data => data.Mass));
columns.Add(("Size", 1,
data => () => $"{data.Shape.Width}x{data.Shape.Height}",
data => data.Shape.Width*data.Shape.Height));
var items = GameManager.ItemManager.ItemData.GetAll<EquippableItemData>();
if (MinimumSizeFilter.gameObject.activeSelf)
items = items.Where(i =>
!(MinimumSizeFilter.Width.text.Length > 0 && i.Shape.Width < int.Parse(MinimumSizeFilter.Width.text) ||
MinimumSizeFilter.Height.text.Length > 0 && i.Shape.Height < int.Parse(MinimumSizeFilter.Height.text)));
if (MaximumSizeFilter.gameObject.activeSelf)
items = items.Where(i =>
!(MaximumSizeFilter.Width.text.Length > 0 && i.Shape.Width > int.Parse(MaximumSizeFilter.Width.text) ||
MaximumSizeFilter.Height.text.Length > 0 && i.Shape.Height > int.Parse(MaximumSizeFilter.Height.text)));
if (_hardpointFilter.filter != null)
items = items.Where(i => i.HardpointType == _hardpointFilter.type);
foreach (var (_, type) in _behaviorFilters)
{
items = items.Where(i => i.Behaviors.Any(b => type.IsInstanceOfType(b)));
foreach (var field in type.GetFields().Where(f => f.GetCustomAttribute<RuntimeInspectable>() != null))
{
var fieldType = field.FieldType;
if (fieldType == typeof(float))
columns.Add((field.Name, 1, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return () => ((float) field.GetValue(behavior)).SignificantDigits(3);
}, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return (float) field.GetValue(behavior);
}));
else if (fieldType == typeof(int))
columns.Add((field.Name, 1, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return () => ((int) field.GetValue(behavior)).ToString();
}, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return (int) field.GetValue(behavior);
}));
else if (fieldType == typeof(PerformanceStat))
{
columns.Add((field.Name, 1, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return () => ((PerformanceStat) field.GetValue(behavior)).Max.SignificantDigits(3);
}, data =>
{
var behavior = data.Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
return ((PerformanceStat) field.GetValue(behavior)).Max;
}));
}
}
}
columns.Add(("Owned", 1,
data => () => (_targetCargo.ItemsOfType.ContainsKey(data.ID) ? _targetCargo.ItemsOfType[data.ID].Count : 0).ToString(),
data => _targetCargo.ItemsOfType.ContainsKey(data.ID) ? _targetCargo.ItemsOfType[data.ID].Count : 0));
Spreadsheet.ShowData(
columns.Select(x => x.name).ToArray(),
columns.Select(x => x.size).ToArray(),
items.Select(i => new SpreadsheetEntryRow
{
Columns = columns.Select(x => new SpreadsheetEntryColumn
{
Output = x.output(i),
SortKey = x.sortKey(i)
}).ToArray(),
OnClick = () =>
{
Properties.Clear();
Properties.AddItemDataProperties(i);
},
OnDoubleClick = () =>
{
_targetCargo.TryStore(GameManager.ItemManager.CreateInstance(i, .1f, 1));
Populate();
}
}));
}
void Start()
{
FoldoutButton.onClick.AddListener(() =>
{
ContextMenu.Clear();
if(_targetCargo != GameManager.DockingBay)
ContextMenu.AddOption("Docking Bay",
() =>
{
_targetCargo = GameManager.DockingBay;
TargetCargoLabel.text = "Docking Bay";
});
foreach (var ship in GameManager.CurrentShip.Parent.Children.Where(GameManager.PlayerShips.Contains))
{
foreach (var bay in ship.CargoBays.Select((bay, index) => (bay, index)))
ContextMenu.AddOption($"{ship.Name} Bay {bay.index}",
() =>
{
_targetCargo = bay.bay;
TargetCargoLabel.text = $"{ship.Name} Bay {bay.index}";
});
}
});
}
void Update()

View file

@ -120,10 +120,12 @@ public class ContextMenu : MonoBehaviour
pivot.x = mousePosition.x > Screen.width - rect.sizeDelta.x * canvas.scaleFactor ? 1 : 0;
var scaleFactor = canvas.scaleFactor;
_dropdownRight = mousePosition.x < Screen.width - rect.sizeDelta.x * scaleFactor * 2;
var pivotTop = (Parent==null ? mousePosition.y : ForcePosition.y) > (PaddingHeight + _options.Count * OptionHeight) * scaleFactor;
pivot.y = pivotTop ? 1 : 0;
var pos = Parent==null ? mousePosition.y : ForcePosition.y;
var space = (PaddingHeight + _options.Count * OptionHeight) * scaleFactor;
var deltaY = pos < space ? space - pos : PaddingHeight / 2 * scaleFactor;
pivot.y = 1;//pivotTop ? 1 : 0;
rect.pivot = pivot;
rect.position = Parent!=null ? ForcePosition - (pivotTop ? Vector3.zero : Vector3.up * (OptionHeight * scaleFactor)) : (Vector3)mousePosition;
rect.position = (Parent != null ? ForcePosition : (Vector3) mousePosition) + Vector3.up * deltaY;
if (Parent==null)
CancelClickCatcher.gameObject.SetActive(true);
}

View file

@ -94,10 +94,8 @@ public class InventoryPanel : MonoBehaviour
else ContextMenu.AddOption(ship.Name, () => Display(ship));
}
foreach (var bay in GameManager.AvailableCargoBays())
{
ContextMenu.AddOption(bay.Name, () => Display(bay));
}
if(GameManager.DockingBay!=null)
ContextMenu.AddOption(GameManager.DockingBay.Name, () => Display(GameManager.DockingBay));
ContextMenu.Show();
});

View file

@ -403,6 +403,13 @@ public class PropertiesPanel : MonoBehaviour
AddProperty("Durability", () => gearData.Durability.SignificantDigits(Context.GameplaySettings.SignificantDigits));
foreach (var behavior in gearData.Behaviors)
{
if (behavior is StatModifierData statMod)
{
if(Math.Abs(statMod.Modifier.Min - statMod.Modifier.Max) < .001f)
AddProperty("Stat Mod", () => $"{statMod.Stat.Target}:{statMod.Stat.Stat}{(statMod.Type == StatModifierType.Constant ? "+" : "x")}{statMod.Modifier.Min.SignificantDigits(Context.GameplaySettings.SignificantDigits)}");
else
AddProperty("Stat Mod", () => $"{statMod.Stat.Target}:{statMod.Stat.Stat}{(statMod.Type == StatModifierType.Constant ? "+" : "x")}{statMod.Modifier.Min.SignificantDigits(Context.GameplaySettings.SignificantDigits)}-{statMod.Modifier.Max.SignificantDigits(Context.GameplaySettings.SignificantDigits)}");
}
var type = behavior.GetType();
if (type.GetCustomAttribute(typeof(RuntimeInspectable)) != null)
{