2021-01-14 03:20:27 +00:00
|
|
|
using System;
|
2026-08-10 14:57:50 +01:00
|
|
|
using System.Collections;
|
2021-01-14 03:20:27 +00:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2026-08-10 14:57:50 +01:00
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Runtime.Remoting.Contexts;
|
2021-01-14 23:47:08 +00:00
|
|
|
using TMPro;
|
2021-01-14 03:20:27 +00:00
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.UI;
|
2026-09-14 17:39:12 +02:00
|
|
|
using CultMath;
|
|
|
|
|
using static CultMath.math;
|
2021-01-14 03:20:27 +00:00
|
|
|
|
|
|
|
|
public class TradeMenu : MonoBehaviour
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
public ActionGameManager GameManager;
|
|
|
|
|
public ContextMenu ContextMenu;
|
2021-01-19 01:22:56 +00:00
|
|
|
public ConfirmationDialog Dialog;
|
2026-08-10 14:57:50 +01:00
|
|
|
public Button NewFilterButton;
|
2021-01-14 23:47:08 +00:00
|
|
|
public Prototype FilterPrototype;
|
2021-01-14 03:20:27 +00:00
|
|
|
public SizeFilter MinimumSizeFilter;
|
|
|
|
|
public SizeFilter MaximumSizeFilter;
|
2026-08-10 14:57:50 +01:00
|
|
|
public PropertiesPanel Properties;
|
2021-01-14 03:20:27 +00:00
|
|
|
public Spreadsheet Spreadsheet;
|
2021-01-14 23:47:08 +00:00
|
|
|
public TextMeshProUGUI TargetCargoLabel;
|
2026-08-10 14:57:50 +01:00
|
|
|
public Button FoldoutButton;
|
2025-07-18 00:21:33 +01:00
|
|
|
public TextMeshProUGUI CreditsLabel;
|
2021-01-14 03:20:27 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
private EquippedCargoBay _targetCargo;
|
2021-01-14 23:47:08 +00:00
|
|
|
private (ItemFilter filter, HardpointType type) _hardpointFilter;
|
2021-01-17 02:45:52 +00:00
|
|
|
private (ItemFilter filter, SimpleCommodityCategory type) _commodityFilter;
|
|
|
|
|
private (ItemFilter filter, CompoundCommodityCategory type) _compoundCommodityFilter;
|
2026-08-10 14:57:50 +01:00
|
|
|
private List<(ItemFilter filter, Type type)> _behaviorFilters = new List<(ItemFilter filter, Type type)>();
|
2021-01-14 23:47:08 +00:00
|
|
|
|
2021-03-20 04:20:17 +00:00
|
|
|
public EquippedCargoBay Inventory { get; set; }
|
2026-08-10 14:57:50 +01:00
|
|
|
|
2021-01-14 03:20:27 +00:00
|
|
|
private void OnEnable()
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (GameManager.DockedEntity == null) return;
|
|
|
|
|
_targetCargo = GameManager.DockingBay;
|
|
|
|
|
TargetCargoLabel.text = "Docking Bay";
|
|
|
|
|
Properties.GameManager = GameManager;
|
2025-07-18 00:21:33 +01:00
|
|
|
UpdateCreditsLabel();
|
2021-03-20 04:20:17 +00:00
|
|
|
|
|
|
|
|
MinimumSizeFilter.Width.onEndEdit.RemoveAllListeners();
|
2021-01-14 23:47:08 +00:00
|
|
|
MinimumSizeFilter.Width.onEndEdit.AddListener(_ => Populate());
|
2021-03-20 04:20:17 +00:00
|
|
|
|
|
|
|
|
MinimumSizeFilter.Height.onEndEdit.RemoveAllListeners();
|
2021-01-14 23:47:08 +00:00
|
|
|
MinimumSizeFilter.Height.onEndEdit.AddListener(_ => Populate());
|
2021-03-20 04:20:17 +00:00
|
|
|
|
|
|
|
|
MaximumSizeFilter.Width.onEndEdit.RemoveAllListeners();
|
2021-01-14 23:47:08 +00:00
|
|
|
MaximumSizeFilter.Width.onEndEdit.AddListener(_ => Populate());
|
2021-03-20 04:20:17 +00:00
|
|
|
|
|
|
|
|
MaximumSizeFilter.Height.onEndEdit.RemoveAllListeners();
|
2021-01-14 23:47:08 +00:00
|
|
|
MaximumSizeFilter.Height.onEndEdit.AddListener(_ => Populate());
|
2026-08-10 14:57:50 +01:00
|
|
|
|
2021-01-14 23:47:08 +00:00
|
|
|
NewFilterButton.onClick.AddListener(() =>
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
ContextMenu.Clear();
|
|
|
|
|
IEnumerable<HardpointType> hardpointTypes = (HardpointType[]) Enum.GetValues(typeof(HardpointType));
|
|
|
|
|
if (_hardpointFilter.filter != null)
|
|
|
|
|
hardpointTypes = hardpointTypes.Where(x => x != _hardpointFilter.type);
|
|
|
|
|
|
|
|
|
|
IEnumerable<SimpleCommodityCategory> commodityTypes = (SimpleCommodityCategory[]) Enum.GetValues(typeof(SimpleCommodityCategory));
|
|
|
|
|
if (_commodityFilter.filter != null)
|
|
|
|
|
commodityTypes = commodityTypes.Where(x => x != _commodityFilter.type);
|
|
|
|
|
|
|
|
|
|
IEnumerable<CompoundCommodityCategory> compoundCommodityTypes = (CompoundCommodityCategory[]) Enum.GetValues(typeof(CompoundCommodityCategory));
|
|
|
|
|
if (_compoundCommodityFilter.filter != null)
|
|
|
|
|
compoundCommodityTypes = compoundCommodityTypes.Where(x => x != _compoundCommodityFilter.type);
|
|
|
|
|
|
|
|
|
|
ContextMenu.AddDropdown("Gear Type", hardpointTypes
|
|
|
|
|
.Select<HardpointType, (string, Action, bool)>(x => (Enum.GetName(typeof(HardpointType), x), () =>
|
|
|
|
|
{
|
|
|
|
|
if (!_hardpointFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_hardpointFilter.filter = FilterPrototype.Instantiate<ItemFilter>();
|
|
|
|
|
_hardpointFilter.filter.OnDisable += () =>
|
|
|
|
|
{
|
|
|
|
|
_hardpointFilter.filter = null;
|
|
|
|
|
Populate();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if(_commodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_commodityFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_commodityFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
if(_compoundCommodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_compoundCommodityFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_compoundCommodityFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_hardpointFilter.filter.Label.text = $"Hardpoint: {Enum.GetName(typeof(HardpointType), x)}";
|
|
|
|
|
_hardpointFilter.type = x;
|
|
|
|
|
Populate();
|
|
|
|
|
}, true)));
|
|
|
|
|
ContextMenu.AddDropdown("Simple Commodity", commodityTypes
|
|
|
|
|
.Select<SimpleCommodityCategory, (string, Action, bool)>(x => (Enum.GetName(typeof(SimpleCommodityCategory), x), () =>
|
|
|
|
|
{
|
|
|
|
|
if (!_commodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_commodityFilter.filter = FilterPrototype.Instantiate<ItemFilter>();
|
|
|
|
|
_commodityFilter.filter.OnDisable += () =>
|
|
|
|
|
{
|
|
|
|
|
_commodityFilter.filter = null;
|
|
|
|
|
Populate();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if(_hardpointFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_hardpointFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_hardpointFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
if(_compoundCommodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_compoundCommodityFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_compoundCommodityFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_commodityFilter.filter.Label.text = $"Hardpoint: {Enum.GetName(typeof(SimpleCommodityCategory), x)}";
|
|
|
|
|
_commodityFilter.type = x;
|
|
|
|
|
Populate();
|
|
|
|
|
}, true)));
|
|
|
|
|
ContextMenu.AddDropdown("Compound Commodity", compoundCommodityTypes
|
|
|
|
|
.Select<CompoundCommodityCategory, (string, Action, bool)>(x => (Enum.GetName(typeof(CompoundCommodityCategory), x), () =>
|
|
|
|
|
{
|
|
|
|
|
if (!_compoundCommodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_compoundCommodityFilter.filter = FilterPrototype.Instantiate<ItemFilter>();
|
|
|
|
|
_compoundCommodityFilter.filter.OnDisable += () =>
|
|
|
|
|
{
|
|
|
|
|
_compoundCommodityFilter.filter = null;
|
|
|
|
|
Populate();
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
if(_hardpointFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_hardpointFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_hardpointFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
if(_commodityFilter.filter)
|
|
|
|
|
{
|
|
|
|
|
_commodityFilter.filter.GetComponent<Prototype>().ReturnToPool();
|
|
|
|
|
_commodityFilter.filter = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_compoundCommodityFilter.filter.Label.text = $"Hardpoint: {Enum.GetName(typeof(CompoundCommodityCategory), x)}";
|
|
|
|
|
_compoundCommodityFilter.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 += () =>
|
|
|
|
|
{
|
|
|
|
|
_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();
|
2021-01-14 23:47:08 +00:00
|
|
|
});
|
|
|
|
|
Populate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Populate()
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var columns = new List<(string name, int size, Func<(ItemInstance item, ItemData data), Func<string>> output, Func<ItemData, IComparable> sortKey)>();
|
2021-01-14 23:47:08 +00:00
|
|
|
|
|
|
|
|
columns.Add(("Name", 3,
|
2026-08-10 14:57:50 +01:00
|
|
|
x => () => x.item is CraftedItemInstance craftedItemInstance ?
|
|
|
|
|
$"<color=#{ColorUtility.ToHtmlStringRGB(GameManager.ItemManager.GetTier(craftedItemInstance).tier.Color.ToColor())}>{x.data.Name}" :
|
|
|
|
|
x.data.Name,
|
|
|
|
|
data => data.Name));
|
2021-01-14 23:47:08 +00:00
|
|
|
if(_hardpointFilter.filter==null)
|
|
|
|
|
columns.Add(("Type", 2,
|
2021-03-20 04:20:17 +00:00
|
|
|
x => () =>
|
2021-01-17 02:45:52 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (x.data is SimpleCommodityData s) return Enum.GetName(typeof(SimpleCommodityCategory), s.Category);
|
|
|
|
|
if(x.data is CompoundCommodityData c) return Enum.GetName(typeof(CompoundCommodityCategory), c.Category);
|
|
|
|
|
if(x.data is EquippableItemData e) return Enum.GetName(typeof(HardpointType), e.HardpointType);
|
2021-01-17 02:45:52 +00:00
|
|
|
return "None";
|
2026-08-10 14:57:50 +01:00
|
|
|
},
|
|
|
|
|
data =>
|
2021-01-17 02:45:52 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (data is SimpleCommodityData s) return (int) s.Category;
|
2021-01-17 02:45:52 +00:00
|
|
|
var offset = Enum.GetValues(typeof(SimpleCommodityCategory)).Length;
|
2026-08-10 14:57:50 +01:00
|
|
|
if(data is CompoundCommodityData c) return (int) c.Category + offset;
|
2021-01-17 02:45:52 +00:00
|
|
|
offset += Enum.GetValues(typeof(CompoundCommodityCategory)).Length;
|
2026-08-10 14:57:50 +01:00
|
|
|
if(data is EquippableItemData e) return (int) e.HardpointType + offset;
|
2021-01-17 02:45:52 +00:00
|
|
|
return 0;
|
|
|
|
|
}));
|
2021-01-14 23:47:08 +00:00
|
|
|
columns.Add(("Mass", 1,
|
2026-08-10 14:57:50 +01:00
|
|
|
x => () => ActionGameManager.PlayerSettings.Format(x.data.Mass),
|
|
|
|
|
data => data.Mass));
|
2021-01-17 02:45:52 +00:00
|
|
|
columns.Add(("Price", 1,
|
2026-08-10 14:57:50 +01:00
|
|
|
x => () => (x.item is CraftedItemInstance craftedItemInstance ? GameManager.ItemManager.GetPrice(craftedItemInstance) : x.data.Price).ToString("N0"),
|
|
|
|
|
data => data.Price));
|
2021-01-14 23:47:08 +00:00
|
|
|
columns.Add(("Size", 1,
|
2026-08-10 14:57:50 +01:00
|
|
|
x => () => $"{x.data.Shape.Width}x{x.data.Shape.Height}",
|
|
|
|
|
data => data.Shape.Width*data.Shape.Height));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
var items = Inventory.Cargo.Keys
|
Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.
Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.
The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
drew the item's schematic texture and tinted hull hardpoints, which need
the parent document. The tagged Studio hands a drawer only the value, so
those wait on the Studio exposing the open record.
Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
reaches CultMath values and refs. Keyboard layouts need no resolver and
keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.
Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
|
|
|
.Select<ItemInstance, (ItemInstance item, ItemData data)>(ii=>(ii, GameManager.ItemManager.GetData(ii)));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
|
|
|
|
if (MinimumSizeFilter.gameObject.activeSelf)
|
|
|
|
|
items = items.Where(i =>
|
2026-08-10 14:57:50 +01:00
|
|
|
!(MinimumSizeFilter.Width.text.Length > 0 && i.data.Shape.Width < int.Parse(MinimumSizeFilter.Width.text) ||
|
|
|
|
|
MinimumSizeFilter.Height.text.Length > 0 && i.data.Shape.Height < int.Parse(MinimumSizeFilter.Height.text)));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
|
|
|
|
if (MaximumSizeFilter.gameObject.activeSelf)
|
|
|
|
|
items = items.Where(i =>
|
2026-08-10 14:57:50 +01:00
|
|
|
!(MaximumSizeFilter.Width.text.Length > 0 && i.data.Shape.Width > int.Parse(MaximumSizeFilter.Width.text) ||
|
|
|
|
|
MaximumSizeFilter.Height.text.Length > 0 && i.data.Shape.Height > int.Parse(MaximumSizeFilter.Height.text)));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
2021-01-17 02:45:52 +00:00
|
|
|
if(_commodityFilter.filter != null)
|
2026-08-10 14:57:50 +01:00
|
|
|
items = items.Where(i => i.data is SimpleCommodityData s && s.Category == _commodityFilter.type);
|
2021-01-17 02:45:52 +00:00
|
|
|
|
|
|
|
|
if(_compoundCommodityFilter.filter != null)
|
2026-08-10 14:57:50 +01:00
|
|
|
items = items.Where(i => i.data is CompoundCommodityData c && c.Category == _compoundCommodityFilter.type);
|
2021-01-17 02:45:52 +00:00
|
|
|
|
2021-01-14 23:47:08 +00:00
|
|
|
if (_hardpointFilter.filter != null)
|
2026-08-10 14:57:50 +01:00
|
|
|
items = items.Where(i => i.data is EquippableItemData e && e.HardpointType == _hardpointFilter.type);
|
2021-01-14 23:47:08 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
foreach (var (_, type) in _behaviorFilters)
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
items = items.Where(i => i.data is EquippableItemData e && e.Behaviors.Any(b => type.IsInstanceOfType(b)));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
foreach (var field in type.GetFields().Where(f => f.GetCustomAttribute<RuntimeInspectable>() != null))
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var fieldType = field.FieldType;
|
|
|
|
|
if (fieldType == typeof(float))
|
2021-03-20 04:20:17 +00:00
|
|
|
columns.Add((field.Name, 1, x =>
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) x.data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return () => ActionGameManager.PlayerSettings.Format((float) field.GetValue(behavior));
|
|
|
|
|
}, data =>
|
2026-06-16 19:55:50 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return (float) field.GetValue(behavior);
|
2026-06-16 19:55:50 +01:00
|
|
|
}));
|
2026-08-10 14:57:50 +01:00
|
|
|
else if (fieldType == typeof(int))
|
2026-06-16 19:55:50 +01:00
|
|
|
columns.Add((field.Name, 1, x =>
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) x.data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return () => ((int) field.GetValue(behavior)).ToString();
|
|
|
|
|
}, data =>
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return (int) field.GetValue(behavior);
|
2021-01-14 23:47:08 +00:00
|
|
|
}));
|
2026-08-10 14:57:50 +01:00
|
|
|
else if (fieldType == typeof(PerformanceStat))
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2021-03-20 04:20:17 +00:00
|
|
|
columns.Add((field.Name, 1, x =>
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) x.data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return () => ActionGameManager.PlayerSettings.Format(((PerformanceStat) field.GetValue(behavior)).Max);
|
|
|
|
|
}, data =>
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var behavior = ((EquippableItemData) data).Behaviors.FirstOrDefault(b => type.IsInstanceOfType(b));
|
|
|
|
|
return ((PerformanceStat) field.GetValue(behavior)).Max;
|
2021-01-14 23:47:08 +00:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
columns.Add(("Owned", 1,
|
2026-08-10 14:57:50 +01:00
|
|
|
x => () =>
|
|
|
|
|
{
|
|
|
|
|
if (x.data is HullData)
|
Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.
Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.
The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
drew the item's schematic texture and tinted hull hardpoints, which need
the parent document. The tagged Studio hands a drawer only the value, so
those wait on the Studio exposing the open record.
Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
reaches CultMath values and refs. Keyboard layouts need no resolver and
keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.
Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
|
|
|
return GameManager.DockedEntity.Children.Count(s => s.Hull.Data.Key.Equals(GameManager.ItemManager.ItemData.RefOf(x.data).Key) && s is Ship {IsPlayerShip: true}).ToString();
|
2026-08-10 14:57:50 +01:00
|
|
|
if(x.data is SimpleCommodityData)
|
Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.
Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.
The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
drew the item's schematic texture and tinted hull hardpoints, which need
the parent document. The tagged Studio hands a drawer only the value, so
those wait on the Studio exposing the open record.
Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
reaches CultMath values and refs. Keyboard layouts need no resolver and
keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.
Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
|
|
|
return (_targetCargo.ItemsOfType.ContainsKey(GameManager.ItemManager.ItemData.RefOf(x.data).Key) ? _targetCargo.ItemsOfType[GameManager.ItemManager.ItemData.RefOf(x.data).Key].Cast<SimpleCommodity>().Sum(s=>s.Quantity) : 0).ToString();
|
|
|
|
|
return (_targetCargo.ItemsOfType.ContainsKey(GameManager.ItemManager.ItemData.RefOf(x.data).Key) ? _targetCargo.ItemsOfType[GameManager.ItemManager.ItemData.RefOf(x.data).Key].Count : 0).ToString();
|
2026-08-10 14:57:50 +01:00
|
|
|
},
|
|
|
|
|
data =>
|
|
|
|
|
{
|
|
|
|
|
if (data is HullData)
|
Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.
Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.
The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
drew the item's schematic texture and tinted hull hardpoints, which need
the parent document. The tagged Studio hands a drawer only the value, so
those wait on the Studio exposing the open record.
Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
reaches CultMath values and refs. Keyboard layouts need no resolver and
keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.
Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
|
|
|
return GameManager.DockedEntity.Children.Count(s => s.Hull.Data.Key.Equals(GameManager.ItemManager.ItemData.RefOf(data).Key) && s is Ship {IsPlayerShip: true});
|
2026-08-10 14:57:50 +01:00
|
|
|
if(data is SimpleCommodityData)
|
Replace the Database Tools with the Studio and cut Unity over
Cut 8b. The Unity project compiles with 0 errors in 6000.3.24f1 batchmode.
Packages: manifest.json pins org.gamecult.cultlib at cultlib-unity-v1.0.57
and org.gamecult.caching.unity at caching-unity-v1.2.0, next to the 8a
CultMath tag. Aetheria.Shared.Unity references GameCult.CultLib and keeps
noEngineReferences.
The Database Tools (Assets/Scripts/CultCache/Editor) are deleted in this
commit, alongside their five ported drawers in
Assets/Scripts/Editor/CultCacheDrawers.cs, each an
[CultInspectorDrawer] ICultInspectorDrawer:
- InspectableType: popup of the attribute type's [Union] member names,
writing the string.
- InspectableColor: float3 and float4 through ColorField.
- InspectableAnimationCurve: float4[] and BezierCurve through CurveField.
- InspectableTemperature: Kelvin, Celsius and Fahrenheit fields over one
value.
- InspectableSchematicShape: size and cell toggles. The old drawer also
drew the item's schematic texture and tinted hull hardpoints, which need
the parent document. The tagged Studio hands a drawer only the value, so
those wait on the Studio exposing the open record.
Unity call sites:
- ActionGameManager opens the catalog through AetheriaStores; Cut 10 adds
the run and player stores.
- Refs resolve through ItemManager.GetData or Get(ref); orbit, planet and
belt maps key by CultRecordKey; identity is compared by reference or key.
- The PlayerSettings.msgpack reader and writer, which Cut 10 deletes, pass
CultDocumentMessagePackSerialization.OptionsFor, since the embedded run
reaches CultMath values and refs. Keyboard layouts need no resolver and
keep MessagePack's defaults until Cut 10.
- The input map uses the nested action to binding shape.
- PropertiesPanel reads CultInspectorRangeAttribute.
- NameTools loses its legacy name-file writer.
- Slime compares serialized settings instead of the deleted SHA1 helper.
- FieldDriver drops InspectorHeader.
- .gitignore covers the test project's build output.
Builds: Aetheria.Shared, AetherDb, Aetheria.Shared.Tests (6/6), Unity
batchmode (0 errors).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 19:15:20 +02:00
|
|
|
return _targetCargo.ItemsOfType.ContainsKey(GameManager.ItemManager.ItemData.RefOf(data).Key) ? _targetCargo.ItemsOfType[GameManager.ItemManager.ItemData.RefOf(data).Key].Cast<SimpleCommodity>().Sum(s=>s.Quantity) : 0;
|
|
|
|
|
return _targetCargo.ItemsOfType.ContainsKey(GameManager.ItemManager.ItemData.RefOf(data).Key) ? _targetCargo.ItemsOfType[GameManager.ItemManager.ItemData.RefOf(data).Key].Count : 0;
|
2026-08-10 14:57:50 +01:00
|
|
|
}));
|
2021-01-14 23:47:08 +00:00
|
|
|
|
|
|
|
|
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),
|
2026-08-10 14:57:50 +01:00
|
|
|
SortKey = x.sortKey(i.data)
|
2021-01-14 23:47:08 +00:00
|
|
|
}).ToArray(),
|
2026-08-10 14:57:50 +01:00
|
|
|
OnClick = () => Properties.Inspect(i.item),
|
2021-01-14 23:47:08 +00:00
|
|
|
OnDoubleClick = () =>
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
switch (i.item)
|
|
|
|
|
{
|
|
|
|
|
case CraftedItemInstance c:
|
|
|
|
|
Buy(c);
|
|
|
|
|
break;
|
|
|
|
|
case SimpleCommodity s:
|
|
|
|
|
Buy(s, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-14 23:47:08 +00:00
|
|
|
Populate();
|
2021-01-19 01:22:56 +00:00
|
|
|
},
|
|
|
|
|
OnRightClick = () =>
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (i.item is SimpleCommodity s)
|
2021-01-19 01:22:56 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
ContextMenu.Clear();
|
|
|
|
|
ContextMenu.AddOption("Buy Quantity",
|
|
|
|
|
() =>
|
|
|
|
|
{
|
|
|
|
|
int quantity = 1;
|
|
|
|
|
Dialog.Clear();
|
|
|
|
|
Dialog.Title.text = $"Buying {i.data.Name}";
|
|
|
|
|
Dialog.AddField("Quantity",
|
|
|
|
|
() => quantity,
|
|
|
|
|
q => quantity = min(min(q, GameManager.Credits / i.data.Price), s.Quantity));
|
|
|
|
|
Dialog.Show(() =>
|
|
|
|
|
{
|
|
|
|
|
Buy(s,quantity);
|
|
|
|
|
|
|
|
|
|
Populate();
|
|
|
|
|
});
|
|
|
|
|
Dialog.MoveToCursor();
|
|
|
|
|
});
|
|
|
|
|
ContextMenu.Show();
|
2021-01-19 01:22:56 +00:00
|
|
|
}
|
2021-01-14 03:20:27 +00:00
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
2025-07-18 00:21:33 +01:00
|
|
|
|
|
|
|
|
private void UpdateCreditsLabel()
|
|
|
|
|
{
|
|
|
|
|
if (CreditsLabel != null)
|
|
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
CreditsLabel.text = GameManager.Credits.ToString("N0");
|
2026-06-14 04:02:54 +01:00
|
|
|
}
|
2021-01-19 01:22:56 +00:00
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
private void Buy(CraftedItemInstance item)
|
2026-06-25 18:09:33 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var data = GameManager.ItemManager.GetData(item);
|
|
|
|
|
var price = GameManager.ItemManager.GetPrice(item);
|
|
|
|
|
if (price < GameManager.Credits)
|
2021-01-14 23:47:08 +00:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if (data is HullData hullData)
|
|
|
|
|
{
|
|
|
|
|
if (hullData.HullType != HullType.Ship) throw new ArgumentException("Attempted to buy non-ship hull from station, WTF are you doing?!");
|
2026-06-16 22:24:48 +01:00
|
|
|
|
2026-09-17 18:22:22 +02:00
|
|
|
var ship = new Ship(GameManager.ItemManager, GameManager.Zone, item as EquippableItem, GameManager.ItemManager.GameplaySettings.DefaultEntitySettings) { IsPlayerShip = true };
|
2026-08-10 14:57:50 +01:00
|
|
|
ship.SetParent(GameManager.DockedEntity);
|
2026-06-21 09:37:54 +01:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
GameManager.Credits -= data.Price;
|
|
|
|
|
UpdateCreditsLabel();
|
|
|
|
|
}
|
|
|
|
|
else if (Inventory.TryTransferItem(_targetCargo, item))
|
|
|
|
|
{
|
|
|
|
|
GameManager.Credits -= price;
|
|
|
|
|
UpdateCreditsLabel();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Dialog.Clear();
|
|
|
|
|
Dialog.Title.text = "Unable to buy: Insufficient Cargo Space!";
|
|
|
|
|
Dialog.Show();
|
|
|
|
|
Dialog.MoveToCursor();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-06-16 22:24:48 +01:00
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
else
|
2026-06-16 22:24:48 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
Dialog.Clear();
|
|
|
|
|
Dialog.Title.text = "Unable to buy: Insufficient Credits!";
|
|
|
|
|
Dialog.Show();
|
|
|
|
|
Dialog.MoveToCursor();
|
2026-06-16 22:24:48 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
private void Buy(SimpleCommodity simpleCommodity, int quantity)
|
2026-06-16 22:24:48 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
var data = GameManager.ItemManager.GetData(simpleCommodity);
|
|
|
|
|
// Up-rounded integer division from https://stackoverflow.com/a/503201
|
|
|
|
|
int lots = (quantity - 1) / data.MaxStack + 1;
|
|
|
|
|
int remaining = quantity;
|
|
|
|
|
for (int i = 0; i < lots; i++)
|
2026-06-21 21:32:57 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
int q = min(remaining, data.MaxStack);
|
|
|
|
|
if (q * data.Price < GameManager.Credits)
|
|
|
|
|
{
|
|
|
|
|
if (Inventory.TryTransferItem(_targetCargo, simpleCommodity, quantity))
|
2026-06-21 21:32:57 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
GameManager.Credits -= q * data.Price;
|
|
|
|
|
UpdateCreditsLabel();
|
|
|
|
|
remaining -= q;
|
2026-06-21 21:32:57 +01:00
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
else
|
2026-06-21 21:32:57 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
Dialog.Clear();
|
|
|
|
|
Dialog.Title.text = "Unable to buy: Insufficient Cargo Space!";
|
|
|
|
|
Dialog.Show();
|
|
|
|
|
Dialog.MoveToCursor();
|
|
|
|
|
return;
|
2026-06-21 21:32:57 +01:00
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Dialog.Clear();
|
|
|
|
|
Dialog.Title.text = "Unable to buy: Insufficient Credits!";
|
|
|
|
|
Dialog.Show();
|
|
|
|
|
Dialog.MoveToCursor();
|
2026-06-21 21:32:57 +01:00
|
|
|
return;
|
2026-08-10 14:57:50 +01:00
|
|
|
}
|
2026-06-16 22:24:48 +01:00
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
|
2026-06-21 21:36:35 +01:00
|
|
|
}
|
2026-06-16 21:45:56 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
void Start()
|
2026-06-21 21:36:35 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
FoldoutButton.onClick.AddListener(() =>
|
2026-06-21 21:36:35 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
ContextMenu.Clear();
|
|
|
|
|
if(_targetCargo != GameManager.DockingBay)
|
|
|
|
|
ContextMenu.AddOption("Docking Bay",
|
|
|
|
|
() =>
|
|
|
|
|
{
|
|
|
|
|
_targetCargo = GameManager.DockingBay;
|
|
|
|
|
TargetCargoLabel.text = "Docking Bay";
|
|
|
|
|
});
|
|
|
|
|
foreach (var ship in GameManager.CurrentEntity.Parent.Children.Where(e => e is Ship {IsPlayerShip: true}))
|
|
|
|
|
{
|
|
|
|
|
foreach (var bay in ship.CargoBays.Select((bay, index) => (bay, index)))
|
2026-06-21 21:36:35 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
if(_targetCargo != bay.bay)
|
|
|
|
|
{
|
|
|
|
|
ContextMenu.AddOption($"{ship.Name} Bay {bay.index+1}",
|
|
|
|
|
() =>
|
|
|
|
|
{
|
|
|
|
|
_targetCargo = bay.bay;
|
|
|
|
|
TargetCargoLabel.text = $"{ship.Name} Bay {bay.index+1}";
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-06-21 21:36:35 +01:00
|
|
|
}
|
2026-08-10 14:57:50 +01:00
|
|
|
}
|
|
|
|
|
ContextMenu.Show();
|
|
|
|
|
});
|
2026-06-16 21:45:56 +01:00
|
|
|
}
|
|
|
|
|
|
2026-08-10 14:57:50 +01:00
|
|
|
void Update()
|
2026-06-16 21:45:56 +01:00
|
|
|
{
|
2026-08-10 14:57:50 +01:00
|
|
|
|
2026-06-16 21:45:56 +01:00
|
|
|
}
|
2021-01-14 03:20:27 +00:00
|
|
|
}
|