ZJ77PUPSEGVVS6C2RNGGSJCBSOXRR4ZXNSUVB5LL2R5NBPE7TT5QC
A5CUHNRA24QIBOJ2MSSUSXDL6JXVWEVDQS4WDJE46NICUOAVBAZAC
J75RIWLT42QXABT6GQMAVIOP5XDUF7UAGFQUSSKDIULB3LEB22EQC
X6CHZAXRC53ABZQSK5QK5PP3QTNJOSGRFGJHNEQBLRSD2UB32AFQC
VZRSH4U473FCZOP5EXURPXXN5J6F3ZLT435YY7A2JHLG2ZZB5KLQC
7BZ73FLUK5ANK44V7EMAROTHHVE2IGJ65GTA26DRWY65566HMNPAC
AZN3UTBQLTKMGIHW5UCAMLC4GGHG6MAORBQ7OIR2U7NTZ53GWGNQC
W42U55HSXS45MZUEXGCSKLO5UV4CWBQN2IZPCKZPWPDSB56FVZDAC
MML56TWYWB6SUY5JEWHG2PNLAH3JY72PGCEXIGOYE54EAC2WSWBAC
7QKPEDFDFQJFTXM4BFJ4CFHOOZKOI6NKRRSBDLMP7SELPJMFOQRAC
G4XIS2NEM4WHRP2775YBCLFDT7JZZS52CMEHY3PA43QSXL45XNDAC
3UN6NREN32ZBQW4EWI7ED4WVQILCU2EIE7DQGFWZR3OUDCO3ZSPQC
R475KN7MR3OG7EBLVNOO7QRIKXDGY2PIDXXV3HW4KBD5QM7B5OJQC
DTKCWM4J7PFNWAAES3RZHQGDA6PTDNX4TZVOXAKF5V7LCZBI3XUAC
62S5AKAC5VGAT4KZYRO4R6XIHO4CCD3RAUHQSW47VEGVEBNZGTGAC
RJXFDWT7OWTX3DQ7SBJFK6M66AQ2LGGIO32QD6G7VTJJS7U2R7XQC
2RUZ7TTRT7SMZT2V7YWUCCTJJXNRT2LNJ2QBBVN4RMREMOGMGMHQC
XRWOXKR3MD3O73SH33CWRUYMTS6KVWGN6U2AIKNVNGHN37MY7UYQC
RZAMG2H2NY73KZJIV4VXLJHJVSRGJDRWWZJERAI6O7AVDW64JEQQC
426KOWJWOX7ZE24UIJCIHRI5KUZYO6NIGK7KC2HOONFDX242UYIQC
GBGS6RTZTCKRMLLDVCBGMRTJNNOSAUTXN5D2EMEQMQAREORE54PAC
L2SE4UCTEU4R7JSHG2J5H6RZPRN4SGBMXK3WCRED2HARX5JEN2AQC
F6P3X2CWO6LA5VH5U64XMPQXIKA3FMDK5MFSKZJFJTFISAL7WKMQC
R6RQ2RKQV3YFYIRN5JED6NAFB4E34WQJRDHHKZ4JO7KLBXRKNFYAC
IXGU2SOXBCQYBV3S5EHZFEFJKSCVUQPVZ3NY7KPG2UHWTEVGRZCAC
Y4T55M6RWGCCDBFO6OPEEVDBO3SZ4F3XESUZJST3LUQYGTIFG72AC
G6KBRZNCPEBI5YF2ZJYRAFCYDO3CDGFPQBDC3I4JQEYJ4DNT234AC
CD5FF75KTOBTMVMTMCKMR6F5DFKOF26I5K43ITNHGBI3ZAZHA4RAC
JMMGK6VQWZOLY6C6YWUPAIHLQXUZ2ZZTVJQAL7ODPZAWUSZQIJJAC
VU32ZIRYU6LCQJFGN4ZA3IQ5HNY5J4PNI5OCPW24L7YBOI3BOTYAC
HXTSBPAP75A7EC4RKWYQMVPPHPNZFPHUORBZWDHGEB6MPAGI7G7AC
3AONCSBYJ6T2HXAZFLCJGGV6SRNYJW3PEJ5GMGN3FYB2NGPVIQIQC
JAZ27QX53QXAN4JVMMX2KYKN4GCULWFMXYSPFZ5HWW4T7FUB5EHAC
S256EPZUSOF4TV2KGOFZDJXUFDD57GZFYTCARFJ3SD24RPC56PHAC
754VCJRLAZAVIQP7STLUIHP3U7PUHBTYAHLNQK4A2H6IKYVNTDKAC
COV4RS6N6Z7PJ5S6LZGNSTP77UJNTKN42OI6K7CBQH6TKCS7ITLQC
using System;
using TagFighter.Effects;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter
{
public class WeaveAction : IAction
{
FollowAction _followAction;
Transform _target;
Weaver _weaver;
RuneWeavingContainer _runeWeaving;
Resources.Range _range;
bool _startedWeaving = false;
static State s_resetState = new(true, 1f, false, default);
bool _disposed = false;
int _lastRange;
EffectContext _context;
ITimeContextContainer _timeContextContainer;
public WeaveAction(Weaver unit, RuneWeavingContainer runeWeaving, Transform target, Resources.Range range) {
if (range == null) {
throw new ArgumentNullException("range");
}
_target = target;
_range = range;
_weaver = unit;
_runeWeaving = runeWeaving;
var currentRange = _range.GetCurrent();
_lastRange = currentRange.AsPrimitive();
_followAction = new(_weaver, _target, currentRange.ToMeter());
_range.ResourceChanged += OnResourceChanged;
_context = new EffectContext(caster: unit.transform, effectLocation: target);
}
void OnResourceChanged(object sender, ResourceChangeArgs e) {
if (_lastRange != e.Current) {
_followAction.Dispose();
_followAction = new(_weaver, _target, _range.GetCurrent().ToMeter());
_lastRange = e.Current;
}
}
public IActionState Advance() {
var stateToReturn = _followAction.Advance();
if (IsInAttackRange()) {
if (_startedWeaving == false) {
Debug.Log("Target got in range, weaving");
_timeContextContainer = _runeWeaving.RuneWeaving.CreateTimeContexts();
}
_startedWeaving = true;
var isAdvanced = _runeWeaving.RuneWeaving.Advance(_timeContextContainer, _context, Time.deltaTime);
stateToReturn = new State(isAdvanced, CompletionStatus(), false, _runeWeaving.RuneWeaving.GetCurrentMove(_timeContextContainer));
}
else {
if (_startedWeaving) {
Debug.Log("Target escaped");
stateToReturn = new State(false, CompletionStatus(), stateToReturn.IsMoving, _runeWeaving.RuneWeaving.GetCurrentMove(_timeContextContainer));
}
}
return stateToReturn;
}
IActionState Cancel() {
_timeContextContainer = null;
return s_resetState;
}
public float CompletionStatus() => _runeWeaving.RuneWeaving.CompletionStatus(_timeContextContainer);
public bool IsSimilarAction(IActionRead action) {
if (action is not WeaveAction other) {
return false;
}
return other != null && _target == other._target && _weaver == other._weaver && _runeWeaving.WeaveName == other._runeWeaving.WeaveName;
}
public override string ToString() {
return $"{_runeWeaving.WeaveName} -> {_target.name}";
}
bool IsInAttackRange() {
// Incorrect in case there are hills. should calc range from feet.
Vector3 source = new(_weaver.transform.position.x, 0, _weaver.transform.position.z);
Vector3 target = new(_target.position.x, 0, _target.position.z);
return Vector3.Distance(source, target) <= _range.GetCurrent().ToMeter();
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) {
Cancel();
_followAction.Dispose();
_followAction = null;
_range.ResourceChanged -= OnResourceChanged;
_range = null;
_runeWeaving = null;
_target = null;
_weaver = null;
}
_disposed = true;
}
}
public readonly struct State : IActionState
{
public bool IsAdvanced { get; }
public float CompletionStatus { get; }
public bool IsMoving { get; }
public CombatMove Move { get; }
public State(bool isAdvanced, float completionStatus, bool isMoving, CombatMove move) {
IsAdvanced = isAdvanced;
CompletionStatus = completionStatus;
IsMoving = isMoving;
Move = move;
}
}
}
}
using System;
using TagFighter.Effects;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter
{
public class WeaveAction : IAction
{
FollowAction _followAction;
Transform _target;
Weaver _weaver;
RuneWeavingContainer _runeWeaving;
Resources.Range _range;
bool _startedWeaving = false;
static State s_resetState = new(true, 1f, false, default);
bool _disposed = false;
int _lastRange;
EffectContext _context;
ITimeContextContainer _timeContextContainer;
public WeaveAction(Weaver unit, RuneWeavingContainer runeWeaving, Transform target, Resources.Range range) {
if (range == null) {
throw new ArgumentNullException("range");
}
_target = target;
_range = range;
_weaver = unit;
_runeWeaving = runeWeaving;
var currentRange = _range.GetCurrent();
_lastRange = currentRange.AsPrimitive();
_followAction = new(_weaver, _target, currentRange.ToMeter());
_range.ResourceChanged += OnResourceChanged;
_context = new EffectContext(new(unit.transform), new(target));
}
void OnResourceChanged(object sender, ResourceChangeArgs e) {
if (_lastRange != e.Current) {
_followAction.Dispose();
_followAction = new(_weaver, _target, _range.GetCurrent().ToMeter());
_lastRange = e.Current;
}
}
public IActionState Advance() {
var stateToReturn = _followAction.Advance();
if (IsInAttackRange()) {
if (_startedWeaving == false) {
Debug.Log("Target got in range, weaving");
_timeContextContainer = _runeWeaving.RuneWeaving.CreateTimeContexts();
}
_startedWeaving = true;
var isAdvanced = _runeWeaving.RuneWeaving.Advance(_timeContextContainer, _context, Time.deltaTime);
stateToReturn = new State(isAdvanced, CompletionStatus(), false, _runeWeaving.RuneWeaving.GetCurrentMove(_timeContextContainer));
}
else {
if (_startedWeaving) {
Debug.Log("Target escaped");
stateToReturn = new State(false, CompletionStatus(), stateToReturn.IsMoving, _runeWeaving.RuneWeaving.GetCurrentMove(_timeContextContainer));
}
}
return stateToReturn;
}
IActionState Cancel() {
_timeContextContainer = null;
return s_resetState;
}
public float CompletionStatus() => _runeWeaving.RuneWeaving.CompletionStatus(_timeContextContainer);
public bool IsSimilarAction(IActionRead action) {
if (action is not WeaveAction other) {
return false;
}
return other != null && _target == other._target && _weaver == other._weaver && _runeWeaving.WeaveName == other._runeWeaving.WeaveName;
}
public override string ToString() {
return $"{_runeWeaving.WeaveName} -> {_target.name}";
}
bool IsInAttackRange() {
// Incorrect in case there are hills. should calc range from feet.
Vector3 source = new(_weaver.transform.position.x, 0, _weaver.transform.position.z);
Vector3 target = new(_target.position.x, 0, _target.position.z);
return Vector3.Distance(source, target) <= _range.GetCurrent().ToMeter();
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!_disposed) {
if (disposing) {
Cancel();
_followAction.Dispose();
_followAction = null;
_range.ResourceChanged -= OnResourceChanged;
_range = null;
_runeWeaving = null;
_target = null;
_weaver = null;
}
_disposed = true;
}
}
public readonly struct State : IActionState
{
public bool IsAdvanced { get; }
public float CompletionStatus { get; }
public bool IsMoving { get; }
public CombatMove Move { get; }
public State(bool isAdvanced, float completionStatus, bool isMoving, CombatMove move) {
IsAdvanced = isAdvanced;
CompletionStatus = completionStatus;
IsMoving = isMoving;
Move = move;
}
}
}
}
using System.Collections.Generic;
using TagFighter.Resources;
using UnityEngine;
using System;
public interface IEffectSystem
{
void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, IAreaOfEffect areaOfEffect);
void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Transform effectLocation, IAreaOfEffect areaOfEffect);
Color GetEffectColor(IEnumerable<(Type, IUnit)> tags);
Mesh CreateArcMesh(Vector3 direction, float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis);
Mesh CreateQuadMesh(Vector3 direction, float width, float length, Vector3 rotationAxis);
}
using System.Collections.Generic;
using TagFighter.Resources;
using UnityEngine;
using System;
namespace TagFighter
{
public interface IEffectSystem
{
void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, IAreaOfEffect areaOfEffect);
void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, EffectTarget effectLocation, IAreaOfEffect areaOfEffect);
Color GetEffectColor(IEnumerable<(Type, IUnit)> tags);
Mesh CreateArcMesh(float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis);
Mesh CreateQuadMesh(float width, float length, Vector3 rotationAxis);
}
}
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CareBoo.Serially;
using TagFighter.Resources;
using UnityEngine;
[ProvideSourceInfo]
[Serializable]
public class EffectSystem : IEffectSystem
{
// ParticleSystem _rippleOutVfx;
[SerializeField] TagFighter.Effects.EffectColors? _colorMapping;
const string PulseMaterial = "Materials/PulseMaterial2";
class EffectRunner : MonoBehaviour
{
public GameObject? EffectObj;
public Material? Material;
public float PlayTime;
public void Run(GameObject effectObj, Material material, float playTime) {
EffectObj = effectObj;
Material = material;
PlayTime = playTime;
Material.SetFloat("_Phase", 0);
EffectObj.SetActive(true);
StartCoroutine(PlayEffect(material, PlayTime));
}
public IEnumerator PlayEffect(Material material, float playTime) {
float totalTime = 0;
float phase = 0;
while (phase < 0.98) {
totalTime += Time.deltaTime;
phase = totalTime / playTime;
material.SetFloat("_Phase", phase);
yield return null;
}
Destroy(EffectObj);
}
}
public EffectSystem() {
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, IAreaOfEffect areaOfEffect) {
switch (areaOfEffect) {
case SingleTarget: ApplyTagsEffectSingle(); break;
case CircleArea aoe: ApplyTagsEffectRadius(tags, origin, direction, aoe); break;
case ConeArea aoe: ApplyTagsEffectCone(tags, origin, direction, aoe); break;
case PathArea aoe: ApplyTagsEffectPath(tags, origin, direction, aoe); break;
}
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Transform effectLocation, IAreaOfEffect areaOfEffect) {
var directionVector = (effectLocation.position - origin.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
ApplyTagsEffect(tags, origin, direction, areaOfEffect);
}
void ApplyTagsEffectSingle() {
Debug.Log("ApplyTagsEffectSingle");
}
void ApplyTagsEffectPath(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, PathArea areaOfEffect) {
var color = GetEffectColor(tags);
var mesh = CreateQuadMeshForUnit(origin, areaOfEffect.Width, areaOfEffect.Length);
// Add a new to clone the shared resource
Material material = new(Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
void ApplyTagsEffectRadius(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, CircleArea areaOfEffect) {
var color = GetEffectColor(tags);
var mesh = CreateArcMeshForUnit(origin, 360, areaOfEffect.Radius);
// Add a new to clone the shared resource
Material material = new(Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
void ApplyTagsEffectCone(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, ConeArea areaOfEffect) {
Debug.Log("ApplyTagsEffectCone");
var color = GetEffectColor(tags);
var mesh = CreateArcMeshForUnit(origin, areaOfEffect.Angle, areaOfEffect.Radius);
// Add a new to clone the shared resource
Material material = new(Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
public Color GetEffectColor(IEnumerable<(Type, IUnit)> tags) {
Debug.Log("GetEffectColor");
var color = new Color();
Debug.Log($"GetEffectColor length {tags.Count()}");
if (_colorMapping != null) {
foreach (var tag in tags) {
Debug.Log("GetEffectColor tag");
if (_colorMapping.ContainsKey(tag.Item1)) {
Debug.Log("GetEffectColor mapping found");
var mappedColor = _colorMapping[tag.Item1];
// TODO: scale color by amount
color += mappedColor;
}
}
}
Debug.Log("GetEffectColor Finished");
return color;
}
public Mesh CreateArcMeshForUnit(Transform unit, float arc, float length, int numberOfVerticesInArc = 100) {
return CreateArcMesh(unit.forward, arc, length, numberOfVerticesInArc, Vector3.up);
}
public Mesh CreateQuadMeshForUnit(Transform unit, float width, float length) {
return CreateQuadMesh(unit.forward, width, length, Vector3.up);
}
void DisplayEffect(Transform originUnit, Quaternion direction, Mesh mesh, Material material) {
GameObject effectObj = new();
effectObj.SetActive(false);
effectObj.transform.position = originUnit.position;
effectObj.transform.rotation = direction;
var meshRenderer = effectObj.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterial = material;
meshRenderer.shadowCastingMode = 0;
meshRenderer.receiveShadows = false;
var meshFilter = effectObj.AddComponent<MeshFilter>();
meshFilter.mesh = mesh;
effectObj.AddComponent<EffectRunner>();
var effectRunner = effectObj.GetComponent<EffectRunner>();
effectRunner.Run(effectObj, material, 1f);
}
public Mesh CreateArcMesh(Vector3 direction, float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis) {
var origin = Vector3.zero;
Mesh mesh = new();
var n = numberOfVerticesInArc + 1;
var arcStep = arc / (n - 2);
var currentDegrees = -arc / 2;
var vertices = new Vector3[n];
var uv = new Vector2[n];
vertices[0] = origin;
uv[0] = origin;
for (var i = 1; i < n; i++) {
vertices[i] = Quaternion.AngleAxis(currentDegrees, rotationAxis) * direction * length;
uv[i] = new Vector2(0, 1);
//Debug.Log($"Current Degrees: {currentDegrees} -> {vertices[i]}");
currentDegrees += arcStep;
}
mesh.vertices = vertices;
mesh.uv = uv;
var tris = new int[(n - 2) * 3];
for (var i = 0; i < n - 2; i++) {
tris[i * 3] = 0;
tris[i * 3 + 1] = i + 1;
tris[i * 3 + 2] = i + 2;
}
mesh.triangles = tris;
var normals = new Vector3[n];
for (var i = 0; i < n; i++) {
normals[i] = rotationAxis;
}
mesh.normals = normals;
return mesh;
}
public Mesh CreateQuadMesh(Vector3 direction, float width, float length, Vector3 rotationAxis) {
Mesh mesh = new();
var origin = Vector3.zero;
// TODO: how to calculate local rotation? direction
var vertices = new Vector3[4]
{
new Vector3(-width/2, 0, 0) + origin,
new Vector3(width/2, 0, 0) + origin,
new Vector3(-width/2, 0, length) + origin,
new Vector3(width/2, 0, length) + origin
};
mesh.vertices = vertices;
var tris = new int[6]
{
// lower left triangle
0, 2, 1,
// upper right triangle
2, 3, 1
};
mesh.triangles = tris;
var normals = new Vector3[4]
{
rotationAxis,
rotationAxis,
rotationAxis,
rotationAxis
};
mesh.normals = normals;
var uv = new Vector2[4]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
mesh.uv = uv;
return mesh;
}
}
#nullable enable
namespace TagFighter
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using CareBoo.Serially;
using TagFighter.Resources;
using UnityEngine;
[ProvideSourceInfo]
[Serializable]
public class EffectSystem : IEffectSystem
{
// ParticleSystem _rippleOutVfx;
[SerializeField] Effects.EffectColors? _colorMapping;
const string PulseMaterial = "Materials/PulseMaterial2";
class EffectRunner : MonoBehaviour
{
public GameObject? EffectObj;
public Material? Material;
public float PlayTime;
public void Run(GameObject effectObj, Material material, float playTime) {
EffectObj = effectObj;
Material = material;
PlayTime = playTime;
Material.SetFloat("_Phase", 0);
EffectObj.SetActive(true);
StartCoroutine(PlayEffect(material, PlayTime));
}
public IEnumerator PlayEffect(Material material, float playTime) {
float totalTime = 0;
float phase = 0;
while (phase < 0.98) {
totalTime += Time.deltaTime;
phase = totalTime / playTime;
material.SetFloat("_Phase", phase);
yield return null;
}
Destroy(EffectObj);
}
}
public EffectSystem() {
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, IAreaOfEffect areaOfEffect) {
switch (areaOfEffect) {
case SingleTarget: ApplyTagsEffectSingle(); break;
case CircleArea aoe: ApplyTagsEffectRadius(tags, origin, direction, aoe); break;
case ConeArea aoe: ApplyTagsEffectCone(tags, origin, direction, aoe); break;
case PathArea aoe: ApplyTagsEffectPath(tags, origin, direction, aoe); break;
}
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, EffectTarget effectLocation, IAreaOfEffect areaOfEffect) {
var directionVector = (effectLocation.Transform.position - origin.Transform.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
ApplyTagsEffect(tags, origin, direction, areaOfEffect);
}
void ApplyTagsEffectSingle() {
Debug.Log("ApplyTagsEffectSingle");
}
void ApplyTagsEffectPath(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, PathArea areaOfEffect) {
var color = GetEffectColor(tags);
var mesh = CreateQuadMeshForUnit(origin.Transform, areaOfEffect.Width, areaOfEffect.Length);
// Add a new to clone the shared resource
Material material = new(UnityEngine.Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
void ApplyTagsEffectRadius(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, CircleArea areaOfEffect) {
var color = GetEffectColor(tags);
var mesh = CreateArcMeshForUnit(origin.Transform, 360, areaOfEffect.Radius);
// Add a new to clone the shared resource
Material material = new(UnityEngine.Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
void ApplyTagsEffectCone(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, ConeArea areaOfEffect) {
Debug.Log("ApplyTagsEffectCone");
var color = GetEffectColor(tags);
var mesh = CreateArcMeshForUnit(origin.Transform, areaOfEffect.Angle, areaOfEffect.Radius);
// Add a new to clone the shared resource
Material material = new(UnityEngine.Resources.Load<Material>(PulseMaterial));
material.SetColor("_Color", color);
DisplayEffect(origin, direction, mesh, material);
}
public Color GetEffectColor(IEnumerable<(Type, IUnit)> tags) {
Debug.Log("GetEffectColor");
var color = new Color();
Debug.Log($"GetEffectColor length {tags.Count()}");
if (_colorMapping != null) {
foreach (var tag in tags) {
Debug.Log("GetEffectColor tag");
if (_colorMapping.ContainsKey(tag.Item1)) {
Debug.Log("GetEffectColor mapping found");
var mappedColor = _colorMapping[tag.Item1];
// TODO: scale color by amount
color += mappedColor;
}
}
}
Debug.Log("GetEffectColor Finished");
return color;
}
public Mesh CreateArcMeshForUnit(Transform unit, float arc, float length, int numberOfVerticesInArc = 100) {
return CreateArcMesh(arc, length, numberOfVerticesInArc, Vector3.up);
}
public Mesh CreateQuadMeshForUnit(Transform unit, float width, float length) {
return CreateQuadMesh(width, length, Vector3.up);
}
void DisplayEffect(EffectOrigin originUnit, Quaternion direction, Mesh mesh, Material material) {
GameObject effectObj = new();
effectObj.SetActive(false);
effectObj.transform.position = originUnit.Transform.position;
effectObj.transform.rotation = direction;
var meshRenderer = effectObj.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterial = material;
meshRenderer.shadowCastingMode = 0;
meshRenderer.receiveShadows = false;
var meshFilter = effectObj.AddComponent<MeshFilter>();
meshFilter.mesh = mesh;
effectObj.AddComponent<EffectRunner>();
var effectRunner = effectObj.GetComponent<EffectRunner>();
effectRunner.Run(effectObj, material, 1f);
}
public Mesh CreateArcMesh(float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis) {
var origin = Vector3.zero;
var direction = Vector3.forward;
Mesh mesh = new();
var n = numberOfVerticesInArc + 1;
var arcStep = arc / (n - 2);
var currentDegrees = -arc / 2;
var vertices = new Vector3[n];
var uv = new Vector2[n];
vertices[0] = origin;
uv[0] = origin;
for (var i = 1; i < n; i++) {
vertices[i] = Quaternion.AngleAxis(currentDegrees, rotationAxis) * direction * length;
uv[i] = new Vector2(0, 1);
//Debug.Log($"Current Degrees: {currentDegrees} -> {vertices[i]}");
currentDegrees += arcStep;
}
mesh.vertices = vertices;
mesh.uv = uv;
var tris = new int[(n - 2) * 3];
for (var i = 0; i < n - 2; i++) {
tris[i * 3] = 0;
tris[i * 3 + 1] = i + 1;
tris[i * 3 + 2] = i + 2;
}
mesh.triangles = tris;
var normals = new Vector3[n];
for (var i = 0; i < n; i++) {
normals[i] = rotationAxis;
}
mesh.normals = normals;
return mesh;
}
public Mesh CreateQuadMesh(float width, float length, Vector3 rotationAxis) {
Mesh mesh = new();
var origin = Vector3.zero;
// TODO: how to calculate local rotation? direction
var vertices = new Vector3[4]
{
new Vector3(-width/2, 0, 0) + origin,
new Vector3(width/2, 0, 0) + origin,
new Vector3(-width/2, 0, length) + origin,
new Vector3(width/2, 0, length) + origin
};
mesh.vertices = vertices;
var tris = new int[6]
{
// lower left triangle
0, 2, 1,
// upper right triangle
2, 3, 1
};
mesh.triangles = tris;
var normals = new Vector3[4]
{
rotationAxis,
rotationAxis,
rotationAxis,
rotationAxis
};
mesh.normals = normals;
var uv = new Vector2[4]
{
new Vector2(0, 0),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(1, 1)
};
mesh.uv = uv;
return mesh;
}
}
}
using System.Collections.Generic;
using TagFighter.Resources;
using UnityEngine;
using System;
class DummyEffectSystem : IEffectSystem
{
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Quaternion direction, IAreaOfEffect areaOfEffect) {
Debug.Log("Dummy:ApplyTagsEffect");
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, Transform origin, Transform effectLocation, IAreaOfEffect areaOfEffect) {
Debug.Log("Dummy:ApplyTagsEffect");
}
public Color GetEffectColor(IEnumerable<(Type, IUnit)> tags) {
Debug.Log("Dummy:GetEffectColor");
return Color.cyan;
}
public Mesh CreateArcMesh(Vector3 direction, float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis) {
Debug.Log("Dummy:CreateArcMesh");
return null;
}
public Mesh CreateQuadMesh(Vector3 direction, float width, float length, Vector3 rotationAxis) {
Debug.Log("Dummy:CreateQuadMesh");
return null;
}
}
namespace TagFighter
{
using System.Collections.Generic;
using TagFighter.Resources;
using UnityEngine;
using System;
class DummyEffectSystem : IEffectSystem
{
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, Quaternion direction, IAreaOfEffect areaOfEffect) {
Debug.Log("Dummy:ApplyTagsEffect");
}
public void ApplyTagsEffect(IEnumerable<(Type, IUnit)> tags, EffectOrigin origin, EffectTarget effectLocation, IAreaOfEffect areaOfEffect) {
Debug.Log("Dummy:ApplyTagsEffect");
}
public Color GetEffectColor(IEnumerable<(Type, IUnit)> tags) {
Debug.Log("Dummy:GetEffectColor");
return Color.cyan;
}
public Mesh CreateArcMesh(float arc, float length, int numberOfVerticesInArc, Vector3 rotationAxis) {
Debug.Log("Dummy:CreateArcMesh");
return null;
}
public Mesh CreateQuadMesh(float width, float length, Vector3 rotationAxis) {
Debug.Log("Dummy:CreateQuadMesh");
return null;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using TagFighter.Effects;
using TagFighter.Martial;
using UnityEngine;
[Serializable]
public class MartialSequence : ISequence
{
[UnityEngine.Serialization.FormerlySerializedAs("combatMoveRefs")]
[SerializeField] List<CombatMoveRef> _combatMoveRefs;
public MartialSequence(IEnumerable<CombatMoveRef> combatMoveRefsSequence) {
_combatMoveRefs = combatMoveRefsSequence.ToList();
}
public bool Advance(ITimeContextContainer timeContextContainer, EffectContext effectContext, float deltaTime) {
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
if (timeContext.CurrentIdx >= _combatMoveRefs.Count) {
Debug.Log($"Executed Martial Sequence {timeContext.CurrentIdx}/{_combatMoveRefs.Count} moves in {timeContext.CurrentTime} seconds");
return false;
}
timeContext.CurrentTime += deltaTime;
while ((timeContext.CurrentIdx < _combatMoveRefs.Count) && (timeContext.CurrentTime - timeContext.LastTime >= _combatMoveRefs[timeContext.CurrentIdx].CombatMove.Speed)) {
Debug.Log($"Finished executing Martial move {_combatMoveRefs[timeContext.CurrentIdx].CombatMove.MoveName} {timeContext.CurrentIdx + 1}/{_combatMoveRefs.Count} at {timeContext.CurrentTime}");
// TODO : Actual Execute Move
timeContext.LastTime += _combatMoveRefs[timeContext.CurrentIdx].CombatMove.Speed;
timeContext.CurrentIdx++;
}
SetReleaseMultiplier(timeContextContainer, effectContext);
return true;
}
void SetReleaseMultiplier(ITimeContextContainer timeContextContainer, EffectContext context) {
var currentMove = GetCurrentMove(timeContextContainer);
if (currentMove == null) {
return;
}
var caster = context.Caster;
var animator = caster.gameObject.GetComponentInChildren<Animator>();
var currentAnimationState = animator.GetCurrentAnimatorStateInfo(0);
var posInAnimation = currentAnimationState.normalizedTime;
//Debug.Log($"posInAnimation {posInAnimation}");
var positionalReleaseMultiplier = 1f;
if ((currentMove.StartReleaseNormalized < posInAnimation) && (posInAnimation < currentMove.EndReleaseNormalized)) {
// this means we're in the maximal release section
positionalReleaseMultiplier = 1f;
}
else if (posInAnimation < currentMove.StartReleaseNormalized) {
// otherwise, get a partial bonus in reletaion to how close we are to the relese
positionalReleaseMultiplier = posInAnimation / currentMove.StartReleaseNormalized;
}
else if (currentMove.EndReleaseNormalized < posInAnimation) {
// otherwise, get a partial bonus in reletaion to how far we are from the release
positionalReleaseMultiplier = (1 - posInAnimation) / (1 - currentMove.EndReleaseNormalized);
}
// Debug.Log($"positionalReleaseMultiplier {positionalReleaseMultiplier}");
context.ReleaseMultiplier = new() {
MatchingAoe = currentMove.MatchingAoe,
MatchingAoeReleaseMultiplier = currentMove.MatchingAoeReleaseMultiplier * positionalReleaseMultiplier,
NonMatchingAoeReleaseMultiplier = currentMove.NonMatchingAoeReleaseMultiplier * positionalReleaseMultiplier,
};
}
public void Simulate() {
}
public CombatMove GetCurrentMove(ITimeContextContainer timeContextContainer) {
if (timeContextContainer == null) {
return null;
}
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
CombatMove move = default;
if (timeContext.CurrentIdx < _combatMoveRefs.Count) {
move = _combatMoveRefs[timeContext.CurrentIdx];
}
return move;
}
public IEnumerable<CombatMoveRef> GetSequence() => _combatMoveRefs;
public float CompletionStatus(ITimeContextContainer timeContextContainer) {
if (timeContextContainer == null) {
return 0;
}
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
return timeContext.CurrentTime / _combatMoveRefs.Sum((move) => move.CombatMove.Speed);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using TagFighter.Effects;
using TagFighter.Martial;
using UnityEngine;
[Serializable]
public class MartialSequence : ISequence
{
[UnityEngine.Serialization.FormerlySerializedAs("combatMoveRefs")]
[SerializeField] List<CombatMoveRef> _combatMoveRefs;
public MartialSequence(IEnumerable<CombatMoveRef> combatMoveRefsSequence) {
_combatMoveRefs = combatMoveRefsSequence.ToList();
}
public bool Advance(ITimeContextContainer timeContextContainer, EffectContext effectContext, float deltaTime) {
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
if (timeContext.CurrentIdx >= _combatMoveRefs.Count) {
Debug.Log($"Executed Martial Sequence {timeContext.CurrentIdx}/{_combatMoveRefs.Count} moves in {timeContext.CurrentTime} seconds");
return false;
}
timeContext.CurrentTime += deltaTime;
while ((timeContext.CurrentIdx < _combatMoveRefs.Count) && (timeContext.CurrentTime - timeContext.LastTime >= _combatMoveRefs[timeContext.CurrentIdx].CombatMove.Speed)) {
Debug.Log($"Finished executing Martial move {_combatMoveRefs[timeContext.CurrentIdx].CombatMove.MoveName} {timeContext.CurrentIdx + 1}/{_combatMoveRefs.Count} at {timeContext.CurrentTime}");
// TODO : Actual Execute Move
timeContext.LastTime += _combatMoveRefs[timeContext.CurrentIdx].CombatMove.Speed;
timeContext.CurrentIdx++;
}
SetReleaseMultiplier(timeContextContainer, effectContext);
return true;
}
void SetReleaseMultiplier(ITimeContextContainer timeContextContainer, EffectContext context) {
var currentMove = GetCurrentMove(timeContextContainer);
if (currentMove == null) {
return;
}
var caster = context.Caster;
var animator = caster.Transform.gameObject.GetComponentInChildren<Animator>();
var currentAnimationState = animator.GetCurrentAnimatorStateInfo(0);
var posInAnimation = currentAnimationState.normalizedTime;
//Debug.Log($"posInAnimation {posInAnimation}");
var positionalReleaseMultiplier = 1f;
if ((currentMove.StartReleaseNormalized < posInAnimation) && (posInAnimation < currentMove.EndReleaseNormalized)) {
// this means we're in the maximal release section
positionalReleaseMultiplier = 1f;
}
else if (posInAnimation < currentMove.StartReleaseNormalized) {
// otherwise, get a partial bonus in reletaion to how close we are to the relese
positionalReleaseMultiplier = posInAnimation / currentMove.StartReleaseNormalized;
}
else if (currentMove.EndReleaseNormalized < posInAnimation) {
// otherwise, get a partial bonus in reletaion to how far we are from the release
positionalReleaseMultiplier = (1 - posInAnimation) / (1 - currentMove.EndReleaseNormalized);
}
// Debug.Log($"positionalReleaseMultiplier {positionalReleaseMultiplier}");
context.ReleaseMultiplier = new() {
MatchingAoe = currentMove.MatchingAoe,
MatchingAoeReleaseMultiplier = currentMove.MatchingAoeReleaseMultiplier * positionalReleaseMultiplier,
NonMatchingAoeReleaseMultiplier = currentMove.NonMatchingAoeReleaseMultiplier * positionalReleaseMultiplier,
};
}
public void Simulate() {
}
public CombatMove GetCurrentMove(ITimeContextContainer timeContextContainer) {
if (timeContextContainer == null) {
return null;
}
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
CombatMove move = default;
if (timeContext.CurrentIdx < _combatMoveRefs.Count) {
move = _combatMoveRefs[timeContext.CurrentIdx];
}
return move;
}
public IEnumerable<CombatMoveRef> GetSequence() => _combatMoveRefs;
public float CompletionStatus(ITimeContextContainer timeContextContainer) {
if (timeContextContainer == null) {
return 0;
}
var timeContext = timeContextContainer.GetTimeContext<MartialSequence>();
return timeContext.CurrentTime / _combatMoveRefs.Sum((move) => move.CombatMove.Speed);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using CareBoo.Serially;
using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter.Effects
{
public interface IDelayedEffect
{
void DelayedAction(EffectInput data);
}
public interface IImmediateEffect
{
void ImmediateAction(EffectContext context, IEffect effect);
}
public interface IEffectMode
{
void Apply(EffectContext context);
void Effect(EffectContext context, IEffect effect);
}
[ProvideSourceInfo]
[Serializable]
public class DelayedEffect : IImmediateEffect, IDelayedEffect
{
IEffect _effect;
public void DelayedAction(EffectInput data) {
_effect.Apply(data);
// direction towards target
var directionVector = (data.Context.EffectLocation.position - data.Context.Caster.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
var appliedResource = data.Context.GetAllResourcesInRegister<Added>();
data.Context.EffectSystem.ApplyTagsEffect(appliedResource, data.Context.EffectLocation, direction, data.Context.AreaOfEffect);
}
public void ImmediateAction(EffectContext context, IEffect effect) {
if (effect != null) {
_effect = effect;
context.EffectsToTrigger.Add(this);
}
}
}
[ProvideSourceInfo]
[Serializable]
public class ImmediateEffect : IImmediateEffect
{
[SerializeReference, ShowSerializeReference]
AoeShapes.IAoeShape _areaOfEffect;
public void ImmediateAction(EffectContext context, IEffect effect) {
if (effect != null) {
// direction towards target
var directionVector = (context.EffectLocation.position - context.Caster.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
EffectInput data = new(context, Enumerable.Empty<Transform>(), StatModifierAccessor.Permanent);
var areaOfEffect = _areaOfEffect.AreaOfEffect(data);
// Copy the location of the effect and merge with the direction caster -> target
GameObject tmpGo = new();
tmpGo.transform.position = context.EffectLocation.position;
tmpGo.transform.rotation = direction;
data.Affected = areaOfEffect.GetAffectedUnits(tmpGo.transform);
GameObject.Destroy(tmpGo);
effect.Apply(data);
var appliedResource = context.GetAllResourcesInRegister<Added>();
context.EffectSystem.ApplyTagsEffect(appliedResource, context.EffectLocation, direction, areaOfEffect);
}
}
}
[ProvideSourceInfo]
[Serializable]
public class ImmediateWeave : IImmediateEffect
{
public void ImmediateAction(EffectContext context, IEffect effect) {
EffectInput data = new(context, context.GetAffectedUnits(), StatModifierAccessor.Permanent);
if (effect != null) {
effect.Apply(data);
data.Affected = context.GetAffectedUnits();
}
Materialize(data);
}
void Materialize(EffectInput data) {
foreach (var effect in data.Context.EffectsToTrigger) {
effect.DelayedAction(data);
}
data.Context.EffectsToTrigger.Clear();
}
}
[ProvideSourceInfo]
[Serializable]
public class PeriodicWeave : IImmediateEffect
{
[SerializeReference, ShowSerializeReference]
public AoeShapes.IAoeShape AreaOfEffect;
[SerializeReference, ShowSerializeReference]
public Triggers.ITrigger ApplyTrigger;
[SerializeReference, ShowSerializeReference]
public Triggers.ITrigger EndTrigger;
public bool IsPermanent;
public void ImmediateAction(EffectContext context, IEffect effect) {
EffectInput data = new(context, context.GetAffectedUnits(), StatModifierAccessor.Permanent);
if (effect != null) {
effect.Apply(data);
data.Affected = context.GetAffectedUnits();
}
Materialize(data);
}
void Materialize(EffectInput data) {
var areaOfEffect = AreaOfEffect.AreaOfEffect(data);
/// Move all effects in context to condition context.
List<IDelayedEffect> effectsToTrigger = new(data.Context.EffectsToTrigger);
data.Context.EffectsToTrigger.Clear();
var origin = data.Context.Caster;
foreach (var affectedPawn in data.Affected) {
var condition = IsPermanent ? affectedPawn.gameObject.AddComponent<PawnCondition>() : affectedPawn.gameObject.AddComponent<TransientPawnCondition>();
condition.EndTrigger = EndTrigger?.ShallowCopy();
condition.ApplyTrigger = ApplyTrigger?.ShallowCopy();
condition.Origin = origin;
condition.Context = new EffectContext(caster: affectedPawn, effectLocation: affectedPawn) {
AreaOfEffect = areaOfEffect,
EffectsToTrigger = effectsToTrigger
};
condition.Apply();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using CareBoo.Serially;
using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter.Effects
{
public interface IDelayedEffect
{
void DelayedAction(EffectInput data);
}
public interface IImmediateEffect
{
void ImmediateAction(EffectContext context, IEffect effect);
}
public interface IEffectMode
{
void Apply(EffectContext context);
void Effect(EffectContext context, IEffect effect);
}
[ProvideSourceInfo]
[Serializable]
public class DelayedEffect : IImmediateEffect, IDelayedEffect
{
IEffect _effect;
public void DelayedAction(EffectInput data) {
_effect.Apply(data);
// direction towards target
var directionVector = (data.Context.EffectLocation.Transform.position - data.Context.Caster.Transform.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
var appliedResource = data.Context.GetAllResourcesInRegister<Added>();
data.Context.EffectSystem.ApplyTagsEffect(appliedResource, data.Context.Caster, direction, data.Context.AreaOfEffect);
}
public void ImmediateAction(EffectContext context, IEffect effect) {
if (effect != null) {
_effect = effect;
context.EffectsToTrigger.Add(this);
}
}
}
[ProvideSourceInfo]
[Serializable]
public class ImmediateEffect : IImmediateEffect
{
[SerializeReference, ShowSerializeReference]
AoeShapes.IAoeShape _areaOfEffect;
public void ImmediateAction(EffectContext context, IEffect effect) {
if (effect != null) {
// direction towards target
var directionVector = (context.EffectLocation.Transform.position - context.Caster.Transform.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
EffectInput data = new(context, Enumerable.Empty<Transform>(), StatModifierAccessor.Permanent);
var areaOfEffect = _areaOfEffect.AreaOfEffect(data);
// Copy the location of the effect and merge with the direction caster -> target
GameObject tmpGo = new();
tmpGo.transform.position = context.EffectLocation.Transform.position;
tmpGo.transform.rotation = direction;
data.Affected = areaOfEffect.GetAffectedUnits(new(tmpGo.transform));
GameObject.Destroy(tmpGo);
effect.Apply(data);
var appliedResource = context.GetAllResourcesInRegister<Added>();
context.EffectSystem.ApplyTagsEffect(appliedResource, context.Caster, direction, areaOfEffect);
}
}
}
[ProvideSourceInfo]
[Serializable]
public class ImmediateWeave : IImmediateEffect
{
public void ImmediateAction(EffectContext context, IEffect effect) {
EffectInput data = new(context, context.GetAffectedUnits(), StatModifierAccessor.Permanent);
if (effect != null) {
effect.Apply(data);
data.Affected = context.GetAffectedUnits();
}
Materialize(data);
}
void Materialize(EffectInput data) {
foreach (var effect in data.Context.EffectsToTrigger) {
effect.DelayedAction(data);
}
data.Context.EffectsToTrigger.Clear();
}
}
[ProvideSourceInfo]
[Serializable]
public class PeriodicWeave : IImmediateEffect
{
[SerializeReference, ShowSerializeReference]
public AoeShapes.IAoeShape AreaOfEffect;
[SerializeReference, ShowSerializeReference]
public Triggers.ITrigger ApplyTrigger;
[SerializeReference, ShowSerializeReference]
public Triggers.ITrigger EndTrigger;
public bool IsPermanent;
public void ImmediateAction(EffectContext context, IEffect effect) {
EffectInput data = new(context, context.GetAffectedUnits(), StatModifierAccessor.Permanent);
if (effect != null) {
effect.Apply(data);
data.Affected = context.GetAffectedUnits();
}
Materialize(data);
}
void Materialize(EffectInput data) {
var areaOfEffect = AreaOfEffect.AreaOfEffect(data);
/// Move all effects in context to condition context.
List<IDelayedEffect> effectsToTrigger = new(data.Context.EffectsToTrigger);
data.Context.EffectsToTrigger.Clear();
var origin = data.Context.Caster;
foreach (var affectedPawn in data.Affected) {
var condition = IsPermanent ? affectedPawn.gameObject.AddComponent<PawnCondition>() : affectedPawn.gameObject.AddComponent<TransientPawnCondition>();
condition.EndTrigger = EndTrigger?.ShallowCopy();
condition.ApplyTrigger = ApplyTrigger?.ShallowCopy();
condition.Origin = origin;
condition.Context = new EffectContext(new(affectedPawn), new(affectedPawn)) {
AreaOfEffect = areaOfEffect,
EffectsToTrigger = effectsToTrigger
};
condition.Apply();
}
}
}
}
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;
using TagFighter.Effects.Steps;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter.Effects
{
public class ReleaseMultiplier
{
public Type? MatchingAoe { get; set; }
public float MatchingAoeReleaseMultiplier { get; set; } = 1f;
public float NonMatchingAoeReleaseMultiplier { get; set; } = 1f;
}
public record RegistryKey
{
public Type ContextRegisterType;
public Type ResourceType;
public RegistryKey(Type contextRegisterType, Type resourceType) {
ContextRegisterType = contextRegisterType;
ResourceType = resourceType;
}
}
public class EffectContext
{
Dictionary<RegistryKey, IUnit> _resourceRegistry = new();
public Transform Caster { get; set; }
public Transform EffectLocation { get; set; }
public IAreaOfEffect AreaOfEffect;
public List<IDelayedEffect> EffectsToTrigger { get; set; } = new();
public List<OutputStep<bool>> EffectStepsToTrigger { get; set; } = new();
public IEffectSystem EffectSystem { get; private set; }
public ReleaseMultiplier ReleaseMultiplier { get; set; }
public EffectContext(Transform caster, Transform effectLocation) {
EffectSystem = SystemsHandler.EffectSystem;
ReleaseMultiplier = new();
Caster = caster;
EffectLocation = effectLocation;
AreaOfEffect = new SingleTarget();
}
public Unit<TUnit> GetResource<TResource, TUnit, TContextRegister>()
where TResource : Resource<TUnit>
where TUnit : IUnitType
where TContextRegister : IContextRegister {
var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
var value = (Unit<TUnit>)_resourceRegistry.GetValueOrDefault(key, (Unit<TUnit>)0);
Debug.Log($"{nameof(GetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name}) = {value}");
return value;
}
public Unit<TUnit> SetResource<TResource, TUnit, TContextRegister>(Unit<TUnit> value)
where TResource : Resource<TUnit>
where TUnit : IUnitType
where TContextRegister : IContextRegister {
var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
var newValue = (Unit<TUnit>)(_resourceRegistry[key] = value);
Debug.Log($"{nameof(SetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name}) = {newValue}");
return newValue;
}
public IEnumerable<(Type, IUnit)> GetAllResourcesInRegister<TContextRegister>() where TContextRegister : IContextRegister {
return _resourceRegistry.Where(x => x.Key.ContextRegisterType == typeof(TContextRegister)).Select(x => (x.Key.ResourceType, x.Value));
}
public IEnumerable<Transform> GetAffectedUnits() {
var directionVector = (EffectLocation.position - Caster.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
GameObject tmpGo = new();
tmpGo.transform.position = EffectLocation.position;
tmpGo.transform.rotation = direction;
var affectedUnits = AreaOfEffect.GetAffectedUnits(EffectLocation);
GameObject.Destroy(tmpGo);
return affectedUnits;
}
void ResetEffects() {
EffectsToTrigger.Clear();
EffectStepsToTrigger.Clear();
}
public void Reset() {
ResetEffects();
}
}
}
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using TagFighter.Effects.ResourceLocationAccessors.ContextRegisters;
using TagFighter.Effects.Steps;
using TagFighter.Resources;
using UnityEngine;
namespace TagFighter
{
public struct EffectOrigin
{
public Transform Transform;
public EffectOrigin(Transform transform) {
Transform = transform;
}
}
public struct EffectTarget
{
public Transform Transform;
public EffectTarget(Transform transform) {
Transform = transform;
}
}
}
namespace TagFighter.Effects
{
public class ReleaseMultiplier
{
public Type? MatchingAoe { get; set; }
public float MatchingAoeReleaseMultiplier { get; set; } = 1f;
public float NonMatchingAoeReleaseMultiplier { get; set; } = 1f;
}
public record RegistryKey
{
public Type ContextRegisterType;
public Type ResourceType;
public RegistryKey(Type contextRegisterType, Type resourceType) {
ContextRegisterType = contextRegisterType;
ResourceType = resourceType;
}
}
public class EffectContext
{
Dictionary<RegistryKey, IUnit> _resourceRegistry = new();
public EffectOrigin Caster { get; set; }
public EffectTarget EffectLocation { get; set; }
public IAreaOfEffect AreaOfEffect;
public List<IDelayedEffect> EffectsToTrigger { get; set; } = new();
public List<OutputStep<bool>> EffectStepsToTrigger { get; set; } = new();
public IEffectSystem EffectSystem { get; private set; }
public ReleaseMultiplier ReleaseMultiplier { get; set; }
public EffectContext(EffectOrigin caster, EffectTarget effectLocation) {
EffectSystem = SystemsHandler.EffectSystem;
ReleaseMultiplier = new();
Caster = caster;
EffectLocation = effectLocation;
AreaOfEffect = new SingleTarget();
}
public Unit<TUnit> GetResource<TResource, TUnit, TContextRegister>()
where TResource : Resource<TUnit>
where TUnit : IUnitType
where TContextRegister : IContextRegister {
var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
var value = (Unit<TUnit>)_resourceRegistry.GetValueOrDefault(key, (Unit<TUnit>)0);
Debug.Log($"{nameof(GetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name}) = {value}");
return value;
}
public Unit<TUnit> SetResource<TResource, TUnit, TContextRegister>(Unit<TUnit> value)
where TResource : Resource<TUnit>
where TUnit : IUnitType
where TContextRegister : IContextRegister {
var key = new RegistryKey(typeof(TContextRegister), typeof(TResource));
var newValue = (Unit<TUnit>)(_resourceRegistry[key] = value);
Debug.Log($"{nameof(SetResource)} ({typeof(TResource).Name},{typeof(TContextRegister).Name}) = {newValue}");
return newValue;
}
public IEnumerable<(Type, IUnit)> GetAllResourcesInRegister<TContextRegister>() where TContextRegister : IContextRegister {
return _resourceRegistry.Where(x => x.Key.ContextRegisterType == typeof(TContextRegister)).Select(x => (x.Key.ResourceType, x.Value));
}
public IEnumerable<Transform> GetAffectedUnits() {
var directionVector = (EffectLocation.Transform.position - Caster.Transform.position).normalized;
directionVector.y = 0;
var direction = Quaternion.LookRotation(directionVector, Vector3.up);
GameObject tmpGo = new();
tmpGo.transform.position = EffectLocation.Transform.position;
tmpGo.transform.rotation = direction;
var affectedUnits = AreaOfEffect.GetAffectedUnits(Caster);
GameObject.Destroy(tmpGo);
return affectedUnits;
}
void ResetEffects() {
EffectsToTrigger.Clear();
EffectStepsToTrigger.Clear();
}
public void Reset() {
ResetEffects();
}
}
}
[Serializable]
public class SingleTarget : IAreaOfEffect
{
public IEnumerable<Transform> GetAffectedUnits(Transform origin) {
return new Transform[] { origin };
public interface IAreaOfEffect
{
public IEnumerable<Transform> GetAffectedUnits(EffectOrigin effectOrigin);
public IAreaOfEffect ShallowCopy();
public IAreaOfEffect ShallowCopy() {
return (IAreaOfEffect)MemberwiseClone();
[Serializable]
public class SingleTarget : IAreaOfEffect
{
public IEnumerable<Transform> GetAffectedUnits(EffectOrigin effectOrigin) {
var origin = effectOrigin.Transform;
return new Transform[] { origin };
}
public IAreaOfEffect ShallowCopy() {
return (IAreaOfEffect)MemberwiseClone();
}
}
[Serializable]
public class CircleArea : IAreaOfEffect
{
public float Radius;
public IEnumerable<Transform> GetAffectedUnits(EffectOrigin effectOrigin) {
var origin = effectOrigin.Transform;
var hits = Physics.SphereCastAll(origin.position, Radius, origin.forward, 0, LayerMask.NameToLayer("units"));
[Serializable]
public class CircleArea : IAreaOfEffect
{
public float Radius;
public IEnumerable<Transform> GetAffectedUnits(Transform origin) {
var hits = Physics.SphereCastAll(origin.position, Radius, origin.forward, 0, LayerMask.NameToLayer("units"));
return hits.Select(x => x.transform);
}
public IAreaOfEffect ShallowCopy() {
return (IAreaOfEffect)MemberwiseClone();
}
}
[Serializable]
public class ConeArea : IAreaOfEffect
{
public float Radius;
[Range(0f, 360f)]
public float Angle;
public IEnumerable<Transform> GetAffectedUnits(EffectOrigin effectOrigin) {
var origin = effectOrigin.Transform;
// Its problematic to cast a real cone, so we cast the sphere and later
// calculate if the potential target is within the cone.
var possibleHits = Physics.SphereCastAll(origin.position, Radius, origin.forward, 0, LayerMask.NameToLayer("units"));
// Its problematic to cast a real cone, so we cast the sphere and later
// calculate if the potential target is within the cone.
var possibleHits = Physics.SphereCastAll(origin.position, Radius, origin.forward, 0, LayerMask.NameToLayer("units"));
foreach (var h in possibleHits) {
Debug.Log($"ConeArea:GetAffectedUnits possbile hit: {h.transform.gameObject}");
}
if (possibleHits.Length == 0) {
return Enumerable.Empty<Transform>();
}
var hits = possibleHits.Where(h => {
var targetDir = (h.transform.position - origin.position).normalized;
var targetAngle = Math.Abs(Vector3.Angle(targetDir, origin.forward));
Debug.Log($"ConeArea:GetAffectedUnits target: {h.transform.gameObject} angle: {targetAngle}");
var hits = possibleHits.Where(h => {
var targetDir = (h.transform.position - origin.position).normalized;
var targetAngle = Math.Abs(Vector3.Angle(targetDir, origin.forward));
Debug.Log($"ConeArea:GetAffectedUnits target: {h.transform.gameObject} angle: {targetAngle}");
foreach (var h in hits) {
Debug.Log($"ConeArea:GetAffectedUnits hit: {h.transform.gameObject}");
}
public IAreaOfEffect ShallowCopy() {
return (IAreaOfEffect)MemberwiseClone();
}
}
[Serializable]
public class PathArea : IAreaOfEffect
{
public float Width;
public float Length;
public IEnumerable<Transform> GetAffectedUnits(EffectOrigin effectOrigin) {
var origin = effectOrigin.Transform;
[Serializable]
public class PathArea : IAreaOfEffect
{
public float Width;
public float Length;
public IEnumerable<Transform> GetAffectedUnits(Transform origin) {
// Get potential collisions
var hits = Physics.BoxCastAll(origin.position, new Vector3(Width / 2, 0, 0), origin.forward, origin.rotation, Length, LayerMask.NameToLayer("units"));
// Get potential collisions
var hits = Physics.BoxCastAll(origin.position, new Vector3(Width / 2, 0, 0), origin.forward, origin.rotation, Length, LayerMask.NameToLayer("units"));
foreach (var h in hits) {
Debug.Log($"PathArea:GetAffectedUnits hit: {h.transform.gameObject}");
}
--- !u!114 &-6555239679874132939
--- !u!114 &-6063848963083739922
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5379282b606b40d41b3f107d66364266, type: 3}
m_Name: SequenceTester
m_EditorClassIdentifier:
Position: {x: 242, y: 264}
Guid: 602aeaf6-13dd-4653-8b35-2cec2c5c86e4
_in:
Node: {fileID: -4792174090053785434}
_value:
- 5
- 5
_displayName: Pawn BlueTag
--- !u!114 &-5807716216484026408
--- !u!114 &1913254018762623428
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e3d165e54b32e6d4cbcc75c5b0cf7a68, type: 3}
m_Name: ConeAreaGet
m_EditorClassIdentifier:
Position: {x: 501.1667, y: 510.5}
Guid: 086e1033-2340-463f-afb8-420e1aa3eb29
_radius:
Node: {fileID: -5807716216484026408}
_angle:
Node: {fileID: -8370226476145813476}
- rid: 6877512918992683008
type: {class: EffectSystem, ns: , asm: Assembly-CSharp}
- rid: 1712398209768751105
type: {class: EffectSystem, ns: TagFighter, asm: Assembly-CSharp}
--- !u!114 &1683100427 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 1763275424511731206, guid: 689a4763a3cde4b46a7c011ddb15ea97,
type: 3}
m_PrefabInstance: {fileID: 437407085}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 07f01f81836cb3d488d4164378aa326b, type: 3}
m_Name:
m_EditorClassIdentifier: