Player/PlayerTool.cs
namespace PlanetMeat;
[Group( "Planet Meat - Player" ), Title( "Player Tool" ), Icon( "touch_app" )]
public abstract class PlayerTool : Component, IAttackBlame
{
public static PlayerTool Selected { get; private set; } = null;
public static List<PlayerTool> Toolbox { get; } = [];
private IWorldRing GhostRing
{
get;
set
{
if ( field?.GameObject is GameObject ringGo && ringGo.IsValid )
ringGo.Destroy();
field = value;
}
}
protected virtual IWorldRing NextWorldRing => null;
protected IWorldRing CreateWorldRing( Vector3 position, bool ghost = false )
{
var ring = NextWorldRing;
if ( ring is null )
return null;
ring.WorldPosition = position;
if ( ghost )
ring.MultiplyAlpha( 0.4f );
return ring;
}
protected IWorldRing CreateWorldRing( bool ghost = false ) => CreateWorldRing( WorldPosition, ghost );
private void TryCreateGhostRing()
{
GhostRing = IsSelected && IsUsable ? CreateWorldRing( true ) : null;
}
public static void BakeTools( IEnumerable<PlayerTool> allTools )
{
Toolbox.Clear();
Toolbox.AddRange( allTools.Where( t => t.Available ) );
Toolbox.Sort( SortTools );
}
private static int SortTools( PlayerTool a, PlayerTool b )
{
var aTech = a.Tech;
var bTech = b.Tech;
var aFree = aTech is null;
var bFree = bTech is null;
if ( aFree != bFree )
return aFree ? -1 : 1;
if ( !aFree && !bFree )
{
var costComparison = aTech.Duration.CompareTo( bTech.Duration );
if ( costComparison != 0 )
return costComparison;
}
return a.Ident.CompareTo( b.Ident );
}
public static int IndexToSlotNumber( int i ) => i < 9 ? 1 + i : (i == 9 ? 0 : -1);
public static string IndexToSlotName( int i ) => i < 10 ? "Slot" + IndexToSlotNumber( i ) : "";
public static bool IndexSlotPressed( int i ) => i < 10 && Input.Pressed( IndexToSlotName( i ) );
[Property] public string Ident { get; set; } = "";
[Property] public string Icon { get; set; } = "";
[Property] public string RequiredTech { get; set; } = "";
private BaseTech Tech => Research.Current.GetTech( RequiredTech );
[Property] public string RechargeImprovementIdent { get; set; } = "";
private bool _needsTechCheck = true;
private bool HasTech
{
get
{
if ( _needsTechCheck )
{
_needsTechCheck = false;
field = Tech.Completed;
}
return field;
}
}
public virtual bool Available => HasTech;
public virtual float BaseCooldown => 1.0f;
public ValueAggregator<float> AggregateRecharge => field ??= ValueAggregator<float>.FromImprovement( RechargeImprovementIdent, 1.0f );
public float Cooldown => BaseCooldown / AggregateRecharge.Total;
public float CooldownRemaining { get; private set; }
public bool IsOnCooldown => CooldownRemaining > 0.0f;
public virtual bool IsUsable => !IsOnCooldown;
public virtual bool IsSelectable => true;
public bool IsSelected => this == Selected;
public void BeginUse()
{
if ( IsSelectable && Agenda.Current.Phase == Agenda.Phases.Wave )
{
Selected?.EndUse();
Selected = this;
OnUseBegins();
}
}
protected virtual void OnUseBegins()
{
TryCreateGhostRing();
}
public void EndUse()
{
if ( IsSelected )
Selected = null;
OnUseEnds();
}
protected virtual void OnUseEnds()
{
GhostRing = null;
}
public void ToggleUse()
{
if ( IsSelected )
EndUse();
else
BeginUse();
}
protected override void OnUpdate()
{
base.OnUpdate();
if ( IsOnCooldown )
{
CooldownRemaining -= Time.Delta;
if ( !IsOnCooldown )
EndCooldown();
}
}
public void StartCooldown()
{
CooldownRemaining = Cooldown;
OnCooldownStarted();
}
protected virtual void OnCooldownStarted()
{
GhostRing = null;
}
public void EndCooldown()
{
CooldownRemaining = 0.0f;
OnCooldownEnded();
}
protected virtual void OnCooldownEnded()
{
TryCreateGhostRing();
}
public virtual void OnHover( Vector3 position )
{
GhostRing?.WorldPosition = position;
}
public virtual void OnPlaceStarted( Vector3 position ) { }
public virtual void OnPlaceUpdated( Vector3 fromPosition, Vector3 toPosition ) { }
public virtual void OnPlaceEnded( Vector3 position ) { }
public virtual void OnCancel( Vector3 position )
{
EndUse();
}
}