using System;
using UnityEngine;
using System.Linq;
using Pathfinding.RVO;
namespace Pathfinding.Examples.RTS {
public enum Status {
Invalid,
Failure,
Success,
Running
};
public class BTContext {
public RTSUnit unit;
public Transform transform;
public Animator animator;
}
/// Implements a simple behavior tree. This is the base class for all nodes in the tree.
public abstract class BTNode {
protected Status lastStatus;
public Status Tick (BTContext ctx) {
if (lastStatus == Status.Invalid) OnInit(ctx);
lastStatus = DoTick(ctx);
if (lastStatus == Status.Invalid) throw new System.Exception();
return lastStatus;
}
public void Terminate (BTContext ctx) {
OnTerminate(ctx);
lastStatus = Status.Invalid;
}
protected virtual void OnInit (BTContext ctx) {
}
protected virtual void OnTerminate (BTContext ctx) {
}
protected abstract Status DoTick(BTContext ctx);
}
public class BTTransparent : BTNode {
public BTNode child;
protected override void OnTerminate (BTContext ctx) {
child.Terminate(ctx);
}
protected override Status DoTick (BTContext ctx) {
return child.Tick(ctx);
}
}
public class Once : BTNode {
public BTNode child;
public Once (BTNode child) { this.child = child; }
protected override void OnTerminate (BTContext ctx) {
if (lastStatus == Status.Running) child.Terminate(ctx);
}
protected override Status DoTick (BTContext ctx) {
if (lastStatus == Status.Success) return Status.Success;
var s = child.Tick(ctx);
if (s == Status.Success) {
child.Terminate(ctx);
}
return s;
}
}
public class SimpleAction : BTNode {
public System.Action action;
public SimpleAction (System.Action action) { this.action = action; }
protected override Status DoTick (BTContext ctx) {
action(ctx);
return Status.Success;
}
}
public class Condition : BTNode {
public System.Func predicate;
public Condition (System.Func predicate) { this.predicate = predicate; }
protected override Status DoTick (BTContext ctx) {
return predicate(ctx) ? Status.Success : Status.Failure;
}
}
public class BTSequence : BTNode {
public BTNode[] children;
int childIndex = -1;
public BTSequence (BTNode[] children) {
this.children = children;
}
protected override void OnInit (BTContext ctx) {
childIndex = 0;
}
protected override void OnTerminate (BTContext ctx) {
for (int i = 0; i <= childIndex; i++) {
children[i].Terminate(ctx);
}
childIndex = -1;
}
protected override Status DoTick (BTContext ctx) {
int i;
for (i = 0; i < children.Length; i++) {
var s = children[i].Tick(ctx);
if (s != Status.Success) {
// Terminate all nodes that executed the last frame, but did not execute this frame
for (int j = i + 1; j <= childIndex; j++) children[j].Terminate(ctx);
childIndex = i;
return s;
}
}
childIndex = i - 1;
return Status.Success;
}
}
public class BTSelector : BTNode {
public BTNode[] children;
int childIndex = -1;
public BTSelector (BTNode[] children) {
this.children = children;
}
protected override void OnInit (BTContext ctx) {
}
protected override void OnTerminate (BTContext ctx) {
for (int i = 0; i <= childIndex; i++) {
children[i].Terminate(ctx);
}
childIndex = -1;
}
protected override Status DoTick (BTContext ctx) {
int i;
for (i = 0; i < children.Length; i++) {
var s = children[i].Tick(ctx);
if (s != Status.Failure) {
// Terminate all nodes that executed the last frame, but did not execute this frame
for (int j = i + 1; j <= childIndex; j++) children[j].Terminate(ctx);
childIndex = i;
return s;
}
}
childIndex = i - 1;
return Status.Failure;
}
}
class Binding {
T val;
public System.Func getter;
public System.Action setter;
public T value {
get {
if (getter != null) return getter();
return val;
}
set {
if (setter != null) setter(value);
val = value;
}
}
}
public struct Value {
T val;
Binding binding;
public T value {
get {
if (binding != null) return binding.value;
return val;
}
set {
if (binding != null) binding.value = value;
val = value;
}
}
public void Bind (ref Value other) {
if (other.binding != null && binding == null) binding = other.binding;
else if (binding == null && other.binding != null) other.binding = binding;
else if (binding == null) binding = other.binding = new Binding();
else throw new System.Exception("Too complex binding");
}
public void Bind (System.Func other) {
if (binding != null) throw new System.Exception("Already has a binding");
binding = new Binding();
binding.getter = other;
binding.setter = _ => {
throw new System.InvalidOperationException("Trying to assign a value which has been bound as read-only (using a delegate)");
};
}
public Value Bound {
get {
var r = this;
if (binding == null) Bind(ref r);
return r;
}
}
public Value (System.Func getter) {
val = default(T);
binding = null;
Bind(getter);
}
public static implicit operator Value(System.Func getter) {
var val = new Value();
val.Bind(getter);
return val;
}
}
public class BTMove : BTNode {
public Value destination;
public BTMove (Value destination) {
this.destination = destination;
}
protected override void OnInit (BTContext ctx) {
ctx.unit.SetDestination(destination.value, MovementMode.Move);
}
protected override Status DoTick (BTContext ctx) {
var dest = destination.value;
if ((Time.frameCount % 100) == 0) ctx.unit.SetDestination(dest, MovementMode.Move);
if (VectorMath.SqrDistanceXZ(ctx.transform.position, dest) < 0.5f * 0.5f) {
return Status.Success;
} else {
return Status.Running;
}
}
}
public class FindClosestUnit : BTNode {
public Value target;
public bool reserve;
RTSUnit.Type type;
public FindClosestUnit (RTSUnit.Type type) {
this.type = type;
}
protected override void OnTerminate (BTContext ctx) {
if (reserve && target.value != null) {
if (target.value.reservedBy != ctx.unit) throw new System.Exception();
target.value.reservedBy = null;
}
target.value = null;
}
RTSUnit FindClosest (Vector3 point) {
var units = RTSManager.instance.units.units;
RTSUnit closest = null;
var dist = float.PositiveInfinity;
for (int i = 0; i < units.Count; i++) {
var unit = units[i];
if (unit.type != type || (reserve && unit.reservedBy != null)) {
continue;
}
if (unit.resource != null && !unit.resource.harvestable) {
continue;
}
var d = (unit.transform.position - point).sqrMagnitude;
if (d < dist) {
dist = d;
closest = unit;
}
}
return closest;
}
protected override Status DoTick (BTContext ctx) {
if (target.value != null) {
return Status.Success;
}
target.value = FindClosest(ctx.transform.position);
if (target.value != null) {
if (reserve) target.value.reservedBy = ctx.unit;
return Status.Success;
}
return Status.Failure;
}
}
static class Behaviors {
public static BTNode HarvestBehavior () {
var reserve = new FindClosestUnit(RTSUnit.Type.ResourceCrystal) { reserve = true };
var dropoff = new FindClosestUnit(RTSUnit.Type.HarvesterDropoff) { reserve = true };
var dropoffQueue = new FindClosestUnit(RTSUnit.Type.HarvesterDropoffQueue);
return new BTSelector(new BTNode[] {
new HarvestMode() {
child = new BTSelector(new BTNode[] {
new BTSequence(new BTNode[] {
new Condition(ctx => ctx.unit.storedCrystals > 0),
new BTSequence(new BTNode[] {
dropoff,
new BTMove(new Value(() => dropoff.target.value.transform.position)),
new SimpleAction(ctx => {
ctx.unit.owner.resources.AddResource(RTSUnit.Type.ResourceCrystal, ctx.unit.storedCrystals);
ctx.unit.storedCrystals = 0;
}),
})
//new Deposit(move1),
}),
new BTSequence(new BTNode[] {
new Condition(ctx => ctx.unit.storedCrystals == 0),
new BTSequence(new BTNode[] {
reserve,
new BTMove(new Value(() => reserve.target.value.transform.position)),
new Harvest { resource = new Value(() => reserve.target.value.resource), duration = 5 },
}),
})
})
},
new BTSequence(new BTNode[] {
dropoffQueue,
new BTMove(new Value(() => dropoffQueue.target.value.transform.position)),
})
});
}
}
public class HarvestMode : BTTransparent {
protected override void OnTerminate (BTContext ctx) {
ctx.unit.GetComponent().layer = RVO.RVOLayer.Layer2;
base.OnTerminate(ctx);
}
protected override Status DoTick (BTContext ctx) {
var s = base.DoTick(ctx);
ctx.unit.GetComponent().layer = s == Status.Running ? RVO.RVOLayer.Layer3 : RVO.RVOLayer.Layer2;
return s;
}
}
public class Harvest : BTNode {
public Value resource;
public float duration = 5;
float time;
protected override void OnInit (BTContext ctx) {
ctx.animator.SetBool("harvesting", true);
//ctx.unit.locked = true;
}
protected override void OnTerminate (BTContext ctx) {
Debug.Log("Terminated harvesting");
ctx.animator.SetBool("harvesting", false);
}
protected override Status DoTick (BTContext ctx) {
time += Time.deltaTime;
if (time > duration) {
ctx.animator.SetBool("harvesting", false);
if (ctx.animator.GetCurrentAnimatorStateInfo(0).IsName("RTSHarvesterHarvesting") || ctx.animator.GetCurrentAnimatorStateInfo(0).IsName("RTSHarvesterHarvestingExit")) {
return Status.Running;
} else {
ctx.unit.storedCrystals += 50;
resource.value.value -= 50;
time = 0;
//ctx.unit.locked = false;
return Status.Success;
}
} else {
return Status.Running;
}
}
}
public class BTHarvest {
/*RTSCommandMove moveToDepositPoint;
RTSCommandMove moveToHarvestPoint;
void Deposit () {
}
public override void Tick () {
if (HasResources()) {
if (moveToDepositPoint.Tick() == Success) {
Deposit();
}
} else {
var target = ReserveTarget();
moveToHarvestPoint.target = target;
if (moveToHarvestPoint.Tick() == Success) {
Harvest(target);
}
}
}*/
}
}