Files
john 0de174eb1a feat/teaser-prototype-playable-core (#1)
Co-authored-by: John Lamb <j.lamb13@gmail.com>
Reviewed-on: #1
2026-02-05 02:30:45 +00:00

233 lines
6.2 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Unit : MonoBehaviour
{
public enum Team { Player, Enemy }
public Team team;
public bool isTaggedOut;
public bool hasFlag;
List<Vector2> route = new();
int routeIndex;
bool isMoving;
SpriteRenderer spriteRenderer;
CircleCollider2D circleCollider;
void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
circleCollider = GetComponent<CircleCollider2D>();
}
void Update()
{
if (isTaggedOut || !isMoving || route.Count == 0) return;
FollowRoute();
}
void FollowRoute()
{
if (routeIndex >= route.Count)
{
isMoving = false;
return;
}
Vector2 target = route[routeIndex];
Vector2 current = transform.position;
Vector2 direction = (target - current).normalized;
float distance = Vector2.Distance(current, target);
// Check for obstacles ahead
if (IsObstacleAhead(direction))
{
isMoving = false;
return;
}
float moveDistance = Game.UnitSpeed * Time.deltaTime;
if (moveDistance >= distance)
{
// Reached waypoint
transform.position = new Vector3(target.x, target.y, 0);
routeIndex++;
if (routeIndex >= route.Count)
{
isMoving = false;
}
}
else
{
// Move toward waypoint
Vector2 newPos = current + direction * moveDistance;
transform.position = new Vector3(newPos.x, newPos.y, 0);
}
}
bool IsObstacleAhead(Vector2 direction)
{
// Raycast to check for obstacles (non-trigger colliders)
var hits = Physics2D.RaycastAll(transform.position, direction, 0.6f);
foreach (var hit in hits)
{
if (hit.collider != null && hit.collider.gameObject != gameObject && !hit.collider.isTrigger)
{
return true;
}
}
return false;
}
public void SetRoute(List<Vector2> waypoints)
{
if (isTaggedOut) return;
route = new List<Vector2>(waypoints);
routeIndex = 0;
isMoving = route.Count > 0;
}
public void ClearRoute()
{
route.Clear();
routeIndex = 0;
isMoving = false;
}
void OnTriggerEnter2D(Collider2D other)
{
if (isTaggedOut) return;
// Check for enemy unit collision (tagging)
var otherUnit = other.GetComponent<Unit>();
if (otherUnit != null && otherUnit.team != team && !otherUnit.isTaggedOut)
{
HandleTagCollision(otherUnit);
return;
}
// Check for flag pickup
var flag = other.GetComponent<Flag>();
if (flag != null && flag.team != team && flag.carriedBy == null)
{
flag.Pickup(this);
return;
}
// Check for scoring (reaching own base with enemy flag)
var baseZone = other.GetComponent<BaseZone>();
if (baseZone != null && baseZone.team == team && hasFlag)
{
// Get the flag we're carrying
var carriedFlag = team == Team.Player ? Game.Instance.enemyFlag : Game.Instance.playerFlag;
if (carriedFlag != null && carriedFlag.carriedBy == this)
{
carriedFlag.Drop();
Game.Instance.Score(team);
}
}
}
void HandleTagCollision(Unit other)
{
// Only process collision once - lower instance ID handles it
if (GetInstanceID() > other.GetInstanceID()) return;
// Determine who gets tagged: farther from their own base loses
// The instigator (closer to their base) is NOT captured
Transform myBase = Game.Instance.GetBase(team);
Transform theirBase = Game.Instance.GetBase(other.team);
float myDistance = Vector2.Distance(transform.position, myBase.position);
float theirDistance = Vector2.Distance(other.transform.position, theirBase.position);
// Only the one farther from their base gets tagged
if (myDistance > theirDistance)
{
TagOut();
}
else
{
// They are farther or equal - they get tagged
other.TagOut();
}
}
public void TagOut()
{
if (isTaggedOut) return;
isTaggedOut = true;
isMoving = false;
route.Clear();
// Drop flag if carrying
if (hasFlag)
{
var flag = team == Team.Player ? Game.Instance.enemyFlag : Game.Instance.playerFlag;
if (flag != null && flag.carriedBy == this)
{
flag.Drop();
}
}
// Visual feedback - fade out
if (spriteRenderer != null)
{
var color = spriteRenderer.color;
color.a = 0.3f;
spriteRenderer.color = color;
}
StartCoroutine(RespawnCoroutine());
}
IEnumerator RespawnCoroutine()
{
yield return new WaitForSeconds(Game.RespawnDelay);
// Respawn at base (landscape - horizontal offset toward center)
Transform baseTransform = Game.Instance.GetBase(team);
float xOffset = team == Team.Player ? 2f : -2f;
Vector3 offset = new Vector3(xOffset, Random.Range(-2f, 2f), 0);
transform.position = baseTransform.position + offset;
isTaggedOut = false;
// Restore visual
if (spriteRenderer != null)
{
var color = spriteRenderer.color;
color.a = 1f;
spriteRenderer.color = color;
}
}
public void ForceRespawn(Vector3 position)
{
StopAllCoroutines();
transform.position = position;
isTaggedOut = false;
isMoving = false;
route.Clear();
hasFlag = false;
if (spriteRenderer != null)
{
var color = spriteRenderer.color;
color.a = 1f;
spriteRenderer.color = color;
}
}
public bool IsMoving => isMoving;
public List<Vector2> CurrentRoute => route;
}