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
396 lines
16 KiB
C#
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();
|
|
}
|
|
}
|