Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace OpenRA.Mods.OpenKrush.Mechanics.AttackNotifications.Traits;
using JetBrains.Annotations;
using OpenRA.Traits;
using Primitives;
using Widgets.Ingame;

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
[Desc("Attack notifier which supports per actor notifications.")]
Expand All @@ -30,6 +31,9 @@ public class AdvancedAttackNotifierInfo : TraitInfo
[Desc("Length of time (in ticks) to display a location ping in the minimap.")]
public readonly int RadarPingDuration = 10 * 25;

[Desc("Cooldown in seconds between on-screen attack tooltips.")]
public readonly int TooltipCooldown = 15;

public override object Create(ActorInitializer init)
{
return new AdvancedAttackNotifier(this);
Expand All @@ -40,6 +44,7 @@ public class AdvancedAttackNotifier : INotifyDamage, INotifyCreated
{
private readonly AdvancedAttackNotifierInfo info;
private readonly Dictionary<string, int> lastAttackTimes = new();
private int lastTooltipTick;
private RadarPings? radarPings;

public AdvancedAttackNotifier(AdvancedAttackNotifierInfo info)
Expand All @@ -66,6 +71,12 @@ void INotifyDamage.Damaged(Actor self, AttackInfo attackInfo)
if (attackInfo.Damage.Value == 0)
return;

if (self.World.WorldTick - this.lastTooltipTick >= this.info.TooltipCooldown * 25)
{
this.lastTooltipTick = self.World.WorldTick;
TooltipWidget.Instance?.ShowCentered("Your units are under attack!");
}

var attackNotification = self.TraitOrDefault<AttackNotification>();

if (attackNotification == null)
Expand Down
150 changes: 150 additions & 0 deletions OpenRA.Mods.OpenKrush/Mechanics/Ui/Traits/MovePathDisplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#region Copyright & License Information

/*
* Copyright 2007-2022 The OpenKrush Developers (see AUTHORS)
* This file is part of OpenKrush, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/

#endregion

namespace OpenRA.Mods.OpenKrush.Mechanics.Ui.Traits;

using System.Collections.Generic;
using Common.Traits;
using JetBrains.Annotations;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Traits;

[UsedImplicitly(ImplicitUseTargetFlags.WithMembers)]
[TraitLocation(SystemActors.World)]
[Desc("Draws the planned path of local player units for a short time after a Move order is issued.")]
public class MovePathDisplayInfo : TraitInfo
{
[Desc("Duration in ticks for which the path is displayed.")]
public readonly int DisplayTime = 50;

[Desc("Line color of the displayed path.")]
public readonly Color LineColor = Color.Yellow;

[Desc("Line width in pixels.")]
public readonly int LineWidth = 3;

public override object Create(ActorInitializer init)
{
return new MovePathDisplay(this);
}
}

sealed class PathLine
{
public readonly Actor Actor;
public readonly List<CPos> Cells;
public readonly int ExpireTick;

public PathLine(Actor actor, List<CPos> cells, int expireTick)
{
Actor = actor;
Cells = cells;
ExpireTick = expireTick;
}

public IEnumerable<WPos> Waypoints(World world)
{
var pos = Actor.CenterPosition;
var start = 0;
var bestDist = long.MaxValue;
for (var i = 0; i < Cells.Count; i++)
{
var c = world.Map.CenterOfSubCell(Cells[i], SubCell.FullCell);
var dx = (long)c.X - pos.X;
var dy = (long)c.Y - pos.Y;
var d = dx * dx + dy * dy;
if (d < bestDist)
{
bestDist = d;
start = i;
}
}

yield return pos;
for (var i = start; i < Cells.Count; i++)
yield return world.Map.CenterOfSubCell(Cells[i], SubCell.FullCell);
}
}

public class MovePathDisplay : ITick, IValidateOrder, IRenderAnnotations
{
readonly MovePathDisplayInfo info;
readonly List<PathLine> lines = new();

public MovePathDisplay(MovePathDisplayInfo info)
{
this.info = info;
}

bool IValidateOrder.OrderValidation(OrderManager orderManager, World world, int clientId, Order order)
{
if (order.OrderString != "Move" || order.Subject == null || order.Subject.IsDead || order.Subject.Owner != world.LocalPlayer)
return true;

var mobile = order.Subject.TraitOrDefault<Mobile>();
if (mobile == null || mobile.IsTraitDisabled || order.Target.Type == TargetType.Invalid)
return true;

var targetCell = world.Map.Clamp(world.Map.CellContaining(order.Target.CenterPosition));
if (!mobile.Info.LocomotorInfo.MoveIntoShroud && !world.LocalPlayer.Shroud.IsExplored(targetCell))
return true;

var path = mobile.PathFinder.FindPathToTargetCell(
order.Subject,
new[] { mobile.ToCell },
mobile.NearestMoveableCell(targetCell),
BlockedByActor.All,
laneBias: true);

if (path.Count == 0)
return true;

path.Reverse();

if (mobile.FromCell != mobile.ToCell)
path.Insert(0, mobile.FromCell);

lines.RemoveAll(l => l.Actor == order.Subject);
lines.Add(new PathLine(order.Subject, path, world.WorldTick + info.DisplayTime));

return true;
}

void ITick.Tick(Actor self)
{
lines.RemoveAll(l => l.ExpireTick <= self.World.WorldTick || !l.Actor.IsInWorld || l.Actor.IsDead);
}

IEnumerable<IRenderable> IRenderAnnotations.RenderAnnotations(Actor self, WorldRenderer wr)
{
if (lines.Count == 0)
yield break;

var now = wr.World.WorldTick;
var fadeTicks = System.Math.Max(1, info.DisplayTime / 4);
foreach (var line in lines)
{
var remaining = line.ExpireTick - now;
if (remaining <= 0 || !line.Actor.IsInWorld || line.Actor.IsDead)
continue;

var alpha = 255f * System.Math.Min(1f, remaining / (float)fadeTicks);
var color = Color.FromArgb((int)alpha, info.LineColor);
yield return new TargetLineRenderable(line.Waypoints(wr.World), color, info.LineWidth, 2);
}
}

bool IRenderAnnotations.SpatiallyPartitionable => false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public override bool HandleKeyPress(KeyInput e)
for (var i = 0; i < lastItem; i++)
{
if (e.Key != Game.ModData.Hotkeys[$"Production{i + 1:00}"].GetValue().Key
|| e.Modifiers != Game.ModData.Hotkeys[$"Production{i + 1}"].GetValue().Modifiers)
|| e.Modifiers != Game.ModData.Hotkeys[$"Production{i + 1:00}"].GetValue().Modifiers)
continue;

((ProductionItemButtonWidget)this.Children[i]).ClickedLeft?.Invoke(
Expand Down Expand Up @@ -112,7 +112,8 @@ public override void Tick()
}

button.Visible = this.Active;
button.Bounds.X = (-1 - i) * SidebarButtonWidget.Size;
button.Bounds.X = SidebarWidget.BarWidth - SidebarWidget.GridWidth - this.Bounds.X;
button.Bounds.Y = -this.Bounds.Y + i * SidebarButtonWidget.Size;
}

if (this.Children.Count == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public ProductionCategoryButtonWidget(SidebarWidget sidebar, int index, string[]
: base(sidebar, "unit")
{
this.Categories = categories;
this.Bounds = new(0, index * SidebarButtonWidget.Size, SidebarButtonWidget.Size, SidebarButtonWidget.Size);
this.Bounds = new(index * SidebarButtonWidget.Size, SidebarWidget.GridHeight, SidebarButtonWidget.Size, SidebarButtonWidget.Size);
this.TooltipTitle = label;
this.hotkey = Game.ModData.Hotkeys[$"Production{label}"].GetValue();
}
Expand All @@ -35,6 +35,9 @@ public override bool HandleKeyPress(KeyInput e)
if (!this.IsUsable() || e.Key != this.hotkey.Key || e.IsRepeat || e.Event != KeyInputEvent.Down || e.Modifiers != this.hotkey.Modifiers)
return false;

if (!this.Active && this.Sidebar.AnyCategoryOpen)
return false;

if (!this.Active)
{
this.Active = true;
Expand Down Expand Up @@ -119,7 +122,8 @@ public override void Tick()
for (var i = 0; i < this.Children.Count; i++)
{
var palette = (ProductionPaletteColumnWidget)this.Children[i];
palette.Bounds.X = (i + 1) * SidebarButtonWidget.Size * -1;
palette.Bounds.X = SidebarWidget.BarWidth - SidebarWidget.GridWidth - this.Bounds.X;
palette.Bounds.Y = -this.Bounds.Y;

if (palette.IsFocused)
focused = i;
Expand All @@ -130,7 +134,7 @@ public override void Tick()
else if (focused == -1)
((ProductionPaletteColumnWidget)this.Children[0]).IsFocused = true;

this.Children.ForEach(c => c.Visible = this.Active);
this.Children.ForEach(c => c.Visible = this.Active && ((ProductionPaletteColumnWidget)c).IsFocused);
this.Type = this.Children.Count > 0 ? "unit" : "button";
}

Expand All @@ -147,5 +151,9 @@ public void SelectFactory(Actor factory)

foreach (var productionPalette in productionPalettes)
productionPalette.IsFocused = productionPalette.Queue == productionQueue;

// Queues for buildings/towers/walls are global (on the player actor), so fall back to the first palette.
if (productionQueue == null && productionPalettes.Length > 0)
productionPalettes[0].IsFocused = true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public RadarButtonWidget(SidebarWidget sidebar)
: base(sidebar, "button")
{
this.TooltipTitle = "Radar";
this.Active = true;
}

public override bool HandleKeyPress(KeyInput e)
Expand Down Expand Up @@ -59,19 +60,14 @@ protected override bool IsUsable()

public override void Tick()
{
this.hasRadar = false;
this.hasRadar = true;
var showStances = PlayerRelationship.None;

foreach (var e in this.Sidebar.IngameUi.World.ActorsWithTrait<ProvidesResearchableRadar>()
.Where(p => p.Actor.Owner == this.Sidebar.IngameUi.World.LocalPlayer && !p.Trait.IsTraitDisabled))
{
var researchable = e.Actor.TraitOrDefault<Researchable>();

if (!researchable.IsResearched(ProvidesResearchableRadarInfo.Available))
continue;

this.hasRadar = true;

if (researchable.IsResearched(ProvidesResearchableRadarInfo.ShowAllies))
showStances |= PlayerRelationship.Ally;

Expand All @@ -80,9 +76,6 @@ public override void Tick()
}

this.Sidebar.IngameUi.Radar.ShowStances = showStances;

if (!this.hasRadar)
this.Sidebar.IngameUi.Radar.Visible = this.Active = false;
}

protected override void DrawContents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ public override void MouseEntered()
this.Sidebar.IngameUi.Tooltip.TooltipText = this.TooltipText;

this.Sidebar.IngameUi.Tooltip.Bounds.X = this.RenderBounds.X;
this.Sidebar.IngameUi.Tooltip.Bounds.Y = this.RenderBounds.Y + SidebarButtonWidget.Size / 2;

var tooltipY = this.RenderBounds.Y + SidebarButtonWidget.Size / 2;
if (this.RenderBounds.Y >= Game.Renderer.Resolution.Height - SidebarButtonWidget.Size)
tooltipY = this.RenderBounds.Y - SidebarButtonWidget.Size / 2;

this.Sidebar.IngameUi.Tooltip.Bounds.Y = tooltipY;

this.Sidebar.IngameUi.Tooltip.Visible = true;
}
Expand Down
1 change: 1 addition & 0 deletions OpenRA.Mods.OpenKrush/Widgets/Ingame/IngameUiWidget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public IngameUiWidget(World world, WorldRenderer worldRenderer)
this.Palette = this.WorldRenderer.Palette($"player{this.World.LocalPlayer.InternalName}");

this.AddChild(new StatusWidget(this));
this.AddChild(new SelectionHealthWidget(this));
this.AddChild(this.Radar = new(this));
this.AddChild(new SidebarWidget(this));
this.AddChild(this.Tooltip = new());
Expand Down
Loading