feat: establish mech extraction prototype with bilingual UI
Track Unity source, scenes, editable assets, licenses, tools and QA evidence. Exclude generated caches, Windows builds and raw runtime logs.
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ironspore
|
||||
{
|
||||
public sealed class EnemyUnit : MonoBehaviour
|
||||
{
|
||||
GameSession game;
|
||||
float health, cooldown, age;
|
||||
int type;
|
||||
public void Initialize(GameSession session, int enemyType, GameObject mech, Transform world)
|
||||
{
|
||||
game = session;
|
||||
type = enemyType;
|
||||
health = type == 3 ? 650 : type == 2 ? 140 : 60;
|
||||
var model = Instantiate(mech, transform);
|
||||
model.transform.localScale = Vector3.one * (type == 3 ? 1.7f : type == 0 ? .65f : 1);
|
||||
foreach (var renderer in model.GetComponentsInChildren<Renderer>())
|
||||
renderer.sharedMaterial = WorldFactory.Material(new Color(.5f, .2f, .12f));
|
||||
foreach (var node in model.GetComponentsInChildren<Transform>())
|
||||
node.gameObject.layer = 9;
|
||||
var capsule = gameObject.AddComponent<CapsuleCollider>();
|
||||
capsule.height = type == 3 ? 5.2f : 3;
|
||||
capsule.radius = type == 3 ? 1.5f : .8f;
|
||||
capsule.center = Vector3.up * capsule.height * .5f;
|
||||
WorldFactory.Label(type == 3 ? "world.overseer" : type == 2 ? "world.bulwark" : type == 0 ? "world.skimmer" : "world.sentinel", transform.position + Vector3.up * (capsule.height + 1), new Color(1, .5f, .25f), transform);
|
||||
cooldown = 1 + Random.value;
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
if (!game.InField || game.Paused || game.Trial || !game.World.Player)
|
||||
return;
|
||||
age += Time.deltaTime;
|
||||
Vector3 target = game.World.Player.transform.position + Vector3.up * 1.5f;
|
||||
if (game.World.Escort && game.World.Escort.Health > 0 && Vector3.Distance(transform.position, game.World.Escort.transform.position) < 15)
|
||||
target = game.World.Escort.transform.position;
|
||||
Vector3 flat = target - transform.position;
|
||||
flat.y = 0;
|
||||
if (flat.sqrMagnitude > .1f)
|
||||
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(flat), Time.deltaTime * 2);
|
||||
float distance = flat.magnitude;
|
||||
if (type != 1 && distance > 10)
|
||||
{
|
||||
Vector3 step = flat.normalized * Time.deltaTime * (type == 3 ? 1.4f : type == 0 ? 2.5f : 1.2f);
|
||||
if (!Physics.Raycast(transform.position + Vector3.up, step.normalized, 1.1f, 1, QueryTriggerInteraction.Ignore))
|
||||
transform.position += step;
|
||||
}
|
||||
if (type == 0)
|
||||
{
|
||||
Vector3 p = transform.position;
|
||||
p.y = 1.8f + Mathf.Sin(age) * .4f;
|
||||
transform.position = p;
|
||||
}
|
||||
cooldown -= Time.deltaTime;
|
||||
if (cooldown <= 0 && distance < 42)
|
||||
{
|
||||
Vector3 origin = transform.position + Vector3.up * (type == 3 ? 3.5f : 2);
|
||||
if (!Physics.Linecast(origin, target, 1, QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
var bolt = WorldFactory.Primitive(PrimitiveType.Sphere, "EnemyBolt", origin, Vector3.one * .3f, new Color(1, .35f, .08f), null, false, true);
|
||||
bolt.AddComponent<EnemyBolt>().Initialize((target - origin).normalized, type == 3 ? 23 : 12);
|
||||
}
|
||||
cooldown = type == 3 ? .7f : type == 1 ? 1.7f : 2.5f;
|
||||
}
|
||||
}
|
||||
public void Damage(float damage)
|
||||
{
|
||||
health -= damage;
|
||||
if (health > 0)
|
||||
return;
|
||||
if (type == 3)
|
||||
game.CompleteTerminal();
|
||||
GameAudio.Cue(95, .15f);
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
public sealed class EnemyBolt : MonoBehaviour
|
||||
{
|
||||
Vector3 direction;
|
||||
float damage, life;
|
||||
public void Initialize(Vector3 travel, float amount)
|
||||
{
|
||||
direction = travel;
|
||||
damage = amount;
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
if (!GameSession.Active || !GameSession.Active.InField)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
float distance = Time.deltaTime * 17;
|
||||
if (Physics.SphereCast(transform.position, .12f, direction, out var hit, distance, ~(1 << 9), QueryTriggerInteraction.Ignore))
|
||||
{
|
||||
var pilot = hit.collider.GetComponentInParent<MechPilot>();
|
||||
if (pilot)
|
||||
pilot.Damage(damage);
|
||||
var escort = hit.collider.GetComponentInParent<EscortUnit>();
|
||||
if (escort)
|
||||
escort.Damage(damage);
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
transform.position += direction * distance;
|
||||
life += Time.deltaTime;
|
||||
if (life > 5)
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
public sealed class EscortUnit : MonoBehaviour
|
||||
{
|
||||
GameSession game;
|
||||
public float Health { get; private set; } = 250;
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
game = session;
|
||||
Health = game.Run.escortArmor;
|
||||
WorldFactory.Label("world.escort", transform.position + Vector3.up * 3, Color.cyan, transform);
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
if (!game.InField || game.Paused || Health <= 0 || game.Run.routeCompleted)
|
||||
return;
|
||||
if (Vector3.Distance(game.World.Player.transform.position, transform.position) < 14)
|
||||
transform.position += Vector3.forward * Time.deltaTime * 1.5f;
|
||||
if (transform.position.z >= 33)
|
||||
game.CompleteTerminal();
|
||||
}
|
||||
public void Damage(float amount)
|
||||
{
|
||||
Health = Mathf.Max(0, Health - amount);
|
||||
game.Run.escortArmor = Health;
|
||||
if (Health <= 0)
|
||||
{
|
||||
game.Store.Commit(s => true);
|
||||
game.Notify("notice.escortLost");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user