Skip to content

Commit

Permalink
TS: Implement Hunter-Seeker OpenRA#12918
Browse files Browse the repository at this point in the history
  • Loading branch information
tinix0 committed Mar 31, 2024
1 parent 0c43801 commit 683b6d6
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 11 deletions.
2 changes: 1 addition & 1 deletion OpenRA.Game/GameRules/WeaponInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public bool IsValidTarget(BitSet<TargetableType> targetTypes)
/// <summary>Checks if the weapon is valid against (can target) the target.</summary>
public bool IsValidAgainst(in Target target, World world, Actor firedBy)
{
if (target.Type == TargetType.Actor)
if (target.Type == TargetType.Actor || target.Type == TargetType.TrackedActor)
return IsValidAgainst(target.Actor, firedBy);

if (target.Type == TargetType.FrozenActor)
Expand Down
2 changes: 2 additions & 0 deletions OpenRA.Game/Network/Order.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public static Order Deserialize(World world, BinaryReader r)
{
switch ((TargetType)r.ReadByte())
{
case TargetType.TrackedActor:
case TargetType.Actor:
{
var actorID = r.ReadUInt32();
Expand Down Expand Up @@ -380,6 +381,7 @@ public byte[] Serialize()
w.Write((byte)targetState.Type);
switch (targetState.Type)
{
case TargetType.TrackedActor:
case TargetType.Actor:
w.Write(UIntFromActor(targetState.Actor));
w.Write(targetState.Generation);
Expand Down
1 change: 1 addition & 0 deletions OpenRA.Game/Sync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public static int HashTarget(Target t)
{
switch (t.Type)
{
case TargetType.TrackedActor:
case TargetType.Actor:
return (int)(t.Actor.ActorID << 16) * 0x567;

Expand Down
17 changes: 11 additions & 6 deletions OpenRA.Game/Traits/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace OpenRA.Traits
{
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor, TrackedActor }
public readonly struct Target : IEquatable<Target>
{
public static readonly Target[] None = Array.Empty<Target>();
Expand Down Expand Up @@ -56,9 +56,9 @@ public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
generation = 0;
}

Target(Actor a, int generation)
Target(Actor a, int generation, bool isTracked = false)
{
type = TargetType.Actor;
type = isTracked ? TargetType.TrackedActor : TargetType.Actor;
Actor = a;
this.generation = generation;

Expand Down Expand Up @@ -86,13 +86,14 @@ public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
public static Target FromTargetPositions(in Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); }
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return new Target(w, c, subCell); }
public static Target FromActor(Actor a) { return a != null ? new Target(a, a.Generation) : Invalid; }
public static Target FromTrackedActor(Actor a) { return a != null ? new Target(a, a.Generation, isTracked: true) : Invalid; }
public static Target FromFrozenActor(FrozenActor fa) { return new Target(fa); }

public TargetType Type
{
get
{
if (type == TargetType.Actor)
if (type == TargetType.Actor || type == TargetType.TrackedActor)
{
// Actor is no longer in the world
if (!Actor.IsInWorld || Actor.IsDead)
Expand Down Expand Up @@ -120,6 +121,7 @@ public bool IsValidFor(Actor targeter)
return FrozenActor.IsValid && FrozenActor.Visible && !FrozenActor.Hidden;
case TargetType.Invalid:
return false;
case TargetType.TrackedActor:
case TargetType.Terrain:
default:
return true;
Expand Down Expand Up @@ -158,6 +160,7 @@ public WPos CenterPosition
{
switch (Type)
{
case TargetType.TrackedActor:
case TargetType.Actor:
return Actor.CenterPosition;
case TargetType.FrozenActor:
Expand All @@ -179,6 +182,7 @@ public IEnumerable<WPos> Positions
{
switch (Type)
{
case TargetType.TrackedActor:
case TargetType.Actor:
return Actor.GetTargetablePositions();
case TargetType.FrozenActor:
Expand Down Expand Up @@ -206,6 +210,7 @@ public override string ToString()
{
switch (Type)
{
case TargetType.TrackedActor:
case TargetType.Actor:
return Actor.ToString();

Expand All @@ -232,7 +237,7 @@ public override string ToString()
return me.terrainCenterPosition == other.terrainCenterPosition
&& me.terrainPositions == other.terrainPositions
&& me.cell == other.cell && me.subCell == other.subCell;

case TargetType.TrackedActor:
case TargetType.Actor:
return me.Actor == other.Actor && me.generation == other.generation;

Expand Down Expand Up @@ -263,7 +268,7 @@ public override int GetHashCode()
hash ^= subCell.GetHashCode();

return hash;

case TargetType.TrackedActor:
case TargetType.Actor:
return Actor.GetHashCode() ^ generation.GetHashCode();

Expand Down
70 changes: 70 additions & 0 deletions OpenRA.Mods.Cnc/Traits/HunterSeeker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, 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

using System.Linq;
using OpenRA.Mods.Common.Activities;
using OpenRA.Traits;

namespace OpenRA.Mods.Cnc.Traits
{
sealed class HunterSeekerInfo : TraitInfo
{
[Desc("Valid target relationships.")]
public readonly PlayerRelationship TargetRelationships = PlayerRelationship.Enemy;
public override object Create(ActorInitializer init) { return new HunterSeeker(this); }
}

sealed class HunterSeeker : INotifyAddedToWorld, INotifyBecomingIdle, ITick
{
Actor target;
readonly HunterSeekerInfo info;

public HunterSeeker(HunterSeekerInfo info)
{
this.info = info;
}

void INotifyAddedToWorld.AddedToWorld(Actor self)
{
SelectNewTarget(self);
}

void INotifyBecomingIdle.OnBecomingIdle(Actor self)
{
if (target == null)
return;

if (TargetIsInvalid())
SelectNewTarget(self);
}

bool TargetIsInvalid()
{
return target.Disposed || target.IsDead || !target.IsInWorld;
}

void SelectNewTarget(Actor self)
{
target = self.World.Actors.Where(x => info.TargetRelationships.HasFlag(self.Owner.RelationshipWith(x.Owner)) && x.IsTargetableBy(self)).RandomOrDefault(self.World.SharedRandom);
if (target != null)
self.QueueActivity(false, new FlyAttack(self, Common.Traits.AttackSource.AutoTarget, Target.FromTrackedActor(target), forceAttack: false, null));
}

void ITick.Tick(Actor self)
{
if (self.IsDead)
return;

if (target == null || TargetIsInvalid())
SelectNewTarget(self);
}
}
}
6 changes: 3 additions & 3 deletions OpenRA.Mods.Common/Activities/Air/FlyAttack.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ public FlyAttack(Actor self, AttackSource source, in Target target, bool forceAt
// The target may become hidden between the initial order request and the first tick (e.g. if queued)
// Moving to any position (even if quite stale) is still better than immediately giving up
if ((target.Type == TargetType.Actor && target.Actor.CanBeViewedByPlayer(self.Owner))
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain)
|| target.Type == TargetType.FrozenActor || target.Type == TargetType.Terrain || target.Type == TargetType.TrackedActor)
{
lastVisibleTarget = Target.FromPos(target.CenterPosition);
lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);

if (target.Type == TargetType.Actor)
if (target.Type == TargetType.Actor || target.Type == TargetType.TrackedActor)
{
lastVisibleOwner = target.Actor.Owner;
lastVisibleTargetTypes = target.Actor.GetEnabledTargetTypes();
Expand Down Expand Up @@ -94,7 +94,7 @@ public override bool Tick(Actor self)
attackAircraft.SetRequestedTarget(target, forceAttack);
hasTicked = true;

if (!targetIsHiddenActor && target.Type == TargetType.Actor)
if (target.Type == TargetType.TrackedActor || (!targetIsHiddenActor && (target.Type == TargetType.Actor)))
{
lastVisibleTarget = Target.FromTargetPositions(target);
lastVisibleMaximumRange = attackAircraft.GetMaximumRangeVersusTarget(target);
Expand Down
2 changes: 1 addition & 1 deletion OpenRA.Mods.Common/Warheads/DamageWarhead.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override void DoImpact(in Target target, WarheadArgs args)
var firedBy = args.SourceActor;

// Used by traits or warheads that damage a single actor, rather than a position
if (target.Type == TargetType.Actor)
if (target.Type == TargetType.Actor || target.Type == TargetType.TrackedActor)
{
var victim = target.Actor;

Expand Down
1 change: 1 addition & 0 deletions mods/ts/rules/aircraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -533,5 +533,6 @@ HUNTER:
UseLocation: true
Interactable:
HitShape:
HunterSeeker:
MapEditorData:
Categories: System

0 comments on commit 683b6d6

Please sign in to comment.