Aetheria/Assets/Scripts/UI/ColonyTab.cs
Reinier Vens 1f90d3639a Asteroids & Refactoring
We have proper belts that hug the Grid, reactive properties for planet instance data, and more!
2020-12-01 00:16:11 +00:00

450 lines
20 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;
public ConfirmationDialog ConfirmationDialog;
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
{
Action<Guid, int> showHaulingDialog = (id, maxQuantity) =>
{
var itemData = Context.Cache.Get<ItemData>(id);
ConfirmationDialog.gameObject.SetActive(true);
ConfirmationDialog.Clear();
ConfirmationDialog.Title.text = $"Haul {itemData.Name}";
var quantity = maxQuantity;
var colonies = Context.Cache.GetAll<Entity>()
.Where(e => e.Corporation == entity.Corporation && e is OrbitalEntity && e != entity).ToArray();
var colonyNames = colonies.Select(c => c.Name).ToArray();
ConfirmationDialog.AddField("Quantity", () => quantity, i => quantity = clamp(i, 1, maxQuantity));
var selectedColony = 0;
ConfirmationDialog.AddField("Target Colony", () => selectedColony, i => selectedColony = i,
colonyNames);
ConfirmationDialog.Show(() =>
{
var haulingTask = new HaulingTask
{
Context = Context,
ItemType = id,
Origin = entity.ID,
Quantity = quantity,
Target = colonies[selectedColony].ID,
Zone = entity.Zone.Data.ID
};
Context.Cache.Add(haulingTask);
var corp = Context.Cache.Get<Corporation>(entity.Corporation);
corp.Tasks.Add(haulingTask.ID);
});
};
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 =>
{
if (eventData.button == PointerEventData.InputButton.Left)
{
Details.Clear();
Details.RemoveListener();
Details.AddItemProperties(entity, simpleCommodity);
Details.RefreshValues();
}
else if (eventData.button == PointerEventData.InputButton.Right)
{
ContextMenu.gameObject.SetActive(true);
ContextMenu.Clear();
ContextMenu.AddOption("Haul", () => showHaulingDialog(simpleCommodity.Data, simpleCommodity.Quantity));
ContextMenu.Show();
}
}, 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 maxQuantity = min((int) ((targetEntity.Capacity - targetEntity.OccupiedCapacity) / simpleCommodity.ItemData.Size), simpleCommodity.Quantity);
if (maxQuantity > 0)
{
ConfirmationDialog.gameObject.SetActive(true);
ConfirmationDialog.Clear();
ConfirmationDialog.Title.text = $"Move {simpleCommodity.ItemData.Name}";
var quantity = maxQuantity;
ConfirmationDialog.AddField("Quantity", () => quantity, i => quantity = clamp(i, 1, maxQuantity));
ConfirmationDialog.Show(() =>
{
var newItem = entity.RemoveCargo(simpleCommodity, quantity);
targetEntity.AddCargo(newItem);
});
}
}
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 = Context.CreateShip(gear.ID, Enumerable.Empty<Guid>(),
Enumerable.Empty<Guid>(), entity.Zone, entity.Corporation, _parentEntity.ID, gear.Name);
ship.SetParent(entity);
}
else // Equip Item
entity.Equip(gear.ID, true);
}
}
else if (data.button == PointerEventData.InputButton.Right)
{
ContextMenu.gameObject.SetActive(true);
ContextMenu.Clear();
ContextMenu.AddOption("Haul", () => showHaulingDialog(item.Data, group.Count()));
ContextMenu.Show();
}
};
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();
}
}