Aetheria/Assets/Scripts/UI/ColonyTab.cs
hyperion51 a901a8e829 Ship Building & Drydock
Made it possible to turn new hulls into drones which can then be outfitted and activated for service
Missing blueprint ingredients no longer crash the game and gracefully log what's broken
Loadout cargo is now stored entirely separate from gear
2020-06-11 02:55:11 +01:00

396 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using Unity.Mathematics;
using static Unity.Mathematics.math;
public class ColonyTab : MonoBehaviour
{
public TextMeshProUGUI Title;
public PropertiesPanel General;
public PropertiesPanel Inventory;
public PropertiesPanel ChildInventory;
public PropertiesPanel Details;
public ContextMenu ContextMenu;
[HideInInspector]
public GameContext Context
{
get => _context;
set
{
_context = value;
General.Context = value;
Inventory.Context = value;
ChildInventory.Context = value;
Details.Context = value;
}
}
private GameContext _context;
private Hardpoint _selectedHardpoint;
private Entity _parentEntity;
private Entity _selectedChild;
private IChangeSource _colonyGearChange;
private IChangeSource _colonyCargoChange;
private Action _onColonyInventoryChanged;
private IChangeSource _childGearChange;
private IChangeSource _childCargoChange;
private Action _onChildInventoryChanged;
public void Open(Guid colony)
{
_selectedHardpoint = null;
_parentEntity = Context.Cache.Get<Entity>(colony);
Title.text = _parentEntity.Name;
UpdateGeneral(_parentEntity);
UpdateInventory(_parentEntity, Inventory);
}
private void OnDisable()
{
if (_onColonyInventoryChanged == null)
return;
_colonyGearChange.OnChanged -= _onColonyInventoryChanged;
_colonyCargoChange.OnChanged -= _onColonyInventoryChanged;
Details.Clear();
DeselectChild();
}
private void DeselectChild()
{
if (_onChildInventoryChanged == null)
return;
_childGearChange.OnChanged -= _onChildInventoryChanged;
_childGearChange.OnChanged -= _onChildInventoryChanged;
ChildInventory.Clear();
}
public void UpdateGeneral(Entity entity)
{
General.Clear();
General.AddEntityProperties(entity);
General.AddProperty("Assigned Population", () => $"{entity.AssignedPopulation}/{entity.Population}");
var personalityList = General.AddList("Personality");
foreach (var attribute in entity.Personality.Keys)
personalityList.AddPersonalityProperty(Context.Cache.Get<PersonalityAttribute>(attribute),
() => entity.Personality[attribute]);
personalityList.SetExpanded(false, true);
var childList = General.AddList("Children");
childList.InspectChildren(entity, child =>
{
_selectedChild = child;
UpdateInventory(child, ChildInventory);
});
childList.SetExpanded(false, true);
General.RefreshValues();
}
public void UpdateInventory(Entity entity, PropertiesPanel panel)
{
if (panel == Inventory)
{
_colonyGearChange = entity.GearEvent;
_colonyCargoChange = entity.CargoEvent;
_onColonyInventoryChanged = () => RefreshInventory(entity, panel);
_colonyGearChange.OnChanged += _onColonyInventoryChanged;
_colonyCargoChange.OnChanged += _onColonyInventoryChanged;
RefreshInventory(entity, panel);
}
else
{
DeselectChild();
_childGearChange = entity.GearEvent;
_childCargoChange = entity.CargoEvent;
_onChildInventoryChanged = () => RefreshInventory(entity, panel, true);
_childGearChange.OnChanged += _onChildInventoryChanged;
_childCargoChange.OnChanged += _onChildInventoryChanged;
RefreshInventory(entity, panel, true);
}
}
void RefreshInventory(Entity entity, PropertiesPanel panel, bool child = false)
{
//Debug.Log("Refreshing Inventory!");
var corporation = Context.Cache.Get<Corporation>(entity.Corporation);
var blueprints = corporation.UnlockedBlueprints.Select(id => Context.Cache.Get<BlueprintData>(id));
// Create item data mapping for cargo
var cargoData = entity.Cargo.ToDictionary(id => _context.Cache.Get<ItemInstance>(id),
id => Context.Cache.Get<ItemData>(_context.Cache.Get<ItemInstance>(id).Data));
panel.Clear();
if (child)
{
panel.Title.text = entity.Name;
panel.AddField("Active", () => entity.Active, active => entity.Active = active);
panel.AddSection("General");
panel.AddEntityProperties(entity);
}
panel.AddSection("Hardpoints");
var incompleteGearList = entity.IncompleteGear.Keys
.Select(id => Context.Cache.Get<Gear>(id))
.ToList();
// Skip first hardpoint, that one is always the hull
foreach (var hardpoint in entity.Hardpoints.Skip(1))
{
// These are the items in the inventory which can be equipped to the current hardpoint
var matchingCargoGear = cargoData
.Where(x =>
x.Value is GearData gearData &&
gearData.HardpointType == hardpoint.HardpointData.Type)
.Select(x => x.Key as Gear)
.ToArray();
// Hardpoint has no gear assigned to it
if (hardpoint.Gear == null)
{
// Check whether we're currently building some gear for this hardpoint
var incompleteGear = incompleteGearList.FirstOrDefault(g => g.ItemData.HardpointType == hardpoint.HardpointData.Type);
// We're building something for this hardpoint, show a progress bar!
if (incompleteGear != null)
{
incompleteGearList.Remove(incompleteGear);
panel.AddProgressField(incompleteGear.Name, () =>
{
var productionTime = _context.Cache.Get<BlueprintData>(incompleteGear.Blueprint).ProductionTime;
return (productionTime - (float) entity.IncompleteGear[incompleteGear.ID]) / productionTime;
});
}
else // We're not building anything
{
// These are the blueprints we could build onto this hardpoint directly
var matchingBlueprints = blueprints.Where(bp =>
bp.FactoryItem == Guid.Empty &&
Context.Cache.Get<ItemData>(bp.Item) is GearData gearData &&
gearData.HardpointType == hardpoint.HardpointData.Type).ToArray();
Action<PointerEventData> onClick = null;
if (matchingCargoGear.Any() || matchingBlueprints.Any())
onClick = data =>
{
if (data.button == PointerEventData.InputButton.Left)
PopulateDetails(entity, matchingBlueprints);
else if (data.button == PointerEventData.InputButton.Right)
{
ContextMenu.gameObject.SetActive(true);
ContextMenu.Clear();
if (matchingCargoGear.Any())
ContextMenu.AddDropdown("Install", matchingCargoGear
.Select<Gear, (string, Action, bool)>(gear => (gear.Name, () => entity.Equip(gear, hardpoint), true)));
if (matchingBlueprints.Any())
ContextMenu.AddDropdown("Build", matchingBlueprints
.Select<BlueprintData, (string, Action, bool)>(blueprint =>
(blueprint.Name, () =>
entity.Build(blueprint, 1, _context.Cache.Get<ItemData>(blueprint.Item).Name, true),
entity.GetBlueprintIngredients(blueprint, out _, out _))));
ContextMenu.Show();
}
};
panel.AddProperty($"Empty {Enum.GetName(typeof(HardpointType), hardpoint.HardpointData.Type)} Hardpoint", null, onClick, matchingBlueprints.Any());
}
}
else
{
var prop = panel.AddProperty(hardpoint.Gear.Name, null, data =>
{
if (data.button == PointerEventData.InputButton.Left)
{
if (data.clickCount == 1)
{
_selectedHardpoint = hardpoint;
Details.Inspect(entity, hardpoint);
}
if (data.clickCount == 2)
{
entity.Unequip(hardpoint);
}
}
}, true);
prop.Button.DisableClickWhenSelected = false;
if (hardpoint == _selectedHardpoint)
{
panel.SelectedChild = prop.Button;
prop.Button.CurrentState = FlatButtonState.Selected;
}
}
}
if(!entity.Cargo.Any())
panel.AddSection("No Cargo");
else
{
panel.AddSection("Cargo");
foreach (var itemID in entity.Cargo.Where(id => Context.Cache.Get<ItemInstance>(id) is SimpleCommodity))
{
var simpleCommodity = Context.Cache.Get<SimpleCommodity>(itemID);
var data = simpleCommodity.ItemData;
panel.AddProperty(data.Name, () => simpleCommodity.Quantity.ToString(), eventData =>
{
Details.Clear();
Details.RemoveListener();
Details.AddItemProperties(entity, simpleCommodity);
Details.RefreshValues();
}, true).Button.DragSuccess = eventData =>
{
Entity targetEntity = null;
if (panel == Inventory &&
_selectedChild != null &&
((RectTransform) ChildInventory.transform).ContainsWorldPoint(eventData.position))
targetEntity = _selectedChild;
else if (panel == ChildInventory &&
((RectTransform) Inventory.transform).ContainsWorldPoint(eventData.position))
targetEntity = _parentEntity;
if (targetEntity != null)
{
int quantity = min((int) ((targetEntity.Capacity - targetEntity.OccupiedCapacity) / simpleCommodity.ItemData.Size), simpleCommodity.Quantity);
if (quantity > 0)
{
var newItem = entity.RemoveCargo(simpleCommodity, quantity);
targetEntity.AddCargo(newItem);
return true;
}
}
return false;
};
}
foreach (var group in entity.Cargo
.Select(id => Context.Cache.Get<ItemInstance>(id))
.Where(item => item is CraftedItemInstance)
.Cast<CraftedItemInstance>()
.GroupBy(craftedItem =>
{
var corp = "GameCult";
if (craftedItem.SourceEntity != Guid.Empty)
corp = Context.Cache.Get<Corporation>(Context.Cache.Get<Entity>(craftedItem.SourceEntity).Corporation).Name;
return (craftedItem.Data, craftedItem.Name, corp) ;
}))
{
Action<CraftedItemInstance, PointerEventData> onClick = (item, data) =>
{
if (data.button == PointerEventData.InputButton.Left)
{
if (data.clickCount == 1)
{
Details.Clear();
Details.RemoveListener();
Details.AddItemProperties(entity, item);
Details.RefreshValues();
}
else if (data.clickCount == 2 && item is Gear gear)
{
// Item is a hull, create a ship entity and make it a child
if (gear.ItemData is HullData)
{
entity.RemoveCargo(gear);
var ship = new Ship(Context, gear.ID, Enumerable.Empty<Guid>(), Enumerable.Empty<Guid>(), entity.Zone, entity.Corporation);
ship.Name = gear.Name;
_context.Cache.Add(ship);
_context.SetParent(ship, entity);
}
else // Equip Item
entity.Equip(gear.ID, true);
}
}
};
Func<PointerEventData, CraftedItemInstance, bool> dragSuccess = (data, item) =>
{
Entity targetEntity = null;
if (panel == Inventory &&
_selectedChild != null &&
((RectTransform) ChildInventory.transform).ContainsWorldPoint(data.position))
targetEntity = _selectedChild;
else if (panel == ChildInventory &&
((RectTransform) Inventory.transform).ContainsWorldPoint(data.position))
targetEntity = _parentEntity;
if (targetEntity != null)
{
if (targetEntity.Capacity - targetEntity.OccupiedCapacity >
Context.Cache.Get<ItemData>(item.Data).Size)
{
entity.RemoveCargo(item);
targetEntity.AddCargo(item);
return true;
}
}
return false;
};
if (group.Count() == 1)
{
var item = group.First();
var button = panel.AddProperty(item.Name, null, eventData => onClick(item, eventData), true).Button;
button.DisableClickWhenSelected = false;
button.DragSuccess = data => dragSuccess(data, item);
}
else
{
var instanceList = panel.AddList($"{group.Count().ToString()} {group.Key.Name}");
foreach (var item in group)
{
var button = instanceList.AddProperty(item.Name, null, eventData => onClick(item, eventData), true).Button;
button.DisableClickWhenSelected = false;
button.DragSuccess = data => dragSuccess(data, item);
}
instanceList.SetExpanded(false, true);
}
}
}
panel.RefreshValues();
}
void PopulateDetails(Entity entity, IEnumerable<BlueprintData> blueprints)
{
Details.RemoveListener();
Details.Clear();
foreach (var blueprint in blueprints)
{
var blueprintList = Details.AddList(blueprint.Name);
foreach (var ingredient in blueprint.Ingredients)
{
var itemData = _context.Cache.Get<ItemData>(ingredient.Key);
blueprintList.AddProperty(itemData.Name, () => ingredient.Value.ToString());
}
var bpItemData = _context.Cache.Get<ItemData>(blueprint.Item);
if(entity.GetBlueprintIngredients(blueprint, out _, out _))
blueprintList.AddButton("Build", _ =>
{
entity.Build(blueprint, 1, bpItemData.Name, true);
Details.Clear();
});
blueprintList.RefreshValues();
blueprintList.SetExpanded(false, true);
}
Details.RefreshValues();
}
}