Files
unity/Assets/Game/Scripts/Mechs/MechPilot.cs
T

220 lines
9.1 KiB
C#
Raw Normal View History

using UnityEngine;
namespace Ironspore
{
public sealed class MechPilot : MonoBehaviour
{
public float Health
{
get; private set;
}
public float Energy
{
get; private set;
}
public float HorizontalSpeed
{
get; private set;
}
public int LastDamageFrame { get; private set; } = -1;
public bool FirstPerson
{
get; private set;
}
public bool Grounded => motor && motor.isGrounded;
public float Guard
{
get; private set;
}
GameSession game;
CharacterController motor;
Camera view;
Transform visual;
GameObject cockpit;
float yaw, pitch, vertical, boostRemaining, boostCooldown, shotCooldown, sideCooldown;
Vector3 boostDirection;
bool freshTrigger = true;
float lastHitTime;
public void Initialize(GameSession session, GameObject model)
{
game = session;
motor = gameObject.AddComponent<CharacterController>();
motor.height = 3.15f;
motor.radius = .65f;
motor.center = new Vector3(0, 1.6f, 0);
motor.stepOffset = .45f;
motor.slopeLimit = 55;
gameObject.layer = 8;
visual = new GameObject("MechVisual").transform;
visual.SetParent(transform, false);
var modelInstance = Instantiate(model, visual);
modelInstance.transform.localScale = Vector3.one;
visual.localPosition = Vector3.zero;
foreach (var t in visual.GetComponentsInChildren<Transform>())
t.gameObject.layer = 8;
var cam = new GameObject("PilotCamera");
cam.transform.SetParent(transform);
view = cam.AddComponent<Camera>();
view.nearClipPlane = .08f;
view.farClipPlane = 250;
view.fieldOfView = 70;
cam.AddComponent<AudioListener>();
cockpit = WorldFactory.Cockpit(view.transform);
Health = game.Run.armor;
Energy = game.Run.energy;
ApplyLoadout();
}
public void ApplyLoadout()
{
visual.localScale = game.chassis[game.Run.chassis].modelScale;
}
public void RequireFreshTrigger()
{
freshTrigger = true;
}
void Update()
{
if (!game.InField || game.Paused)
return;
var frame = game.chassis[game.Run.chassis];
float dt = Time.deltaTime;
boostCooldown -= dt;
shotCooldown -= dt;
sideCooldown -= dt;
Guard = Mathf.Max(0, Guard - dt);
Vector2 movement = game.MenuOpen ? Vector2.zero : game.Action("Move").ReadValue<Vector2>();
bool usingEnergy = false;
if (!game.MenuOpen)
{
Vector2 look = game.Action("Look").ReadValue<Vector2>();
yaw += look.x * .105f;
pitch = Mathf.Clamp(pitch - look.y * .105f, -72, 75);
if (game.Action("View").WasPressedThisFrame())
FirstPerson = !FirstPerson;
if (game.Action("Guard").WasPressedThisFrame() && Energy >= 20)
{
Energy -= 20;
Guard = 2.5f;
}
if (game.Action("Boost").WasPressedThisFrame() && boostCooldown <= 0 && Energy >= 18)
{
boostDirection = Quaternion.Euler(0, yaw, 0) * new Vector3(movement.x, 0, movement.y == 0 && movement.x == 0 ? 1 : movement.y);
boostDirection.Normalize();
boostRemaining = frame.boostDuration;
boostCooldown = .65f;
Energy -= 18;
GameAudio.Cue(170, .1f);
}
if (motor.isGrounded && game.Action("Jump").WasPressedThisFrame())
vertical = 7;
if (frame.lift > 0 && game.Action("Jump").IsPressed() && Energy > 0 && !motor.isGrounded)
{
vertical = Mathf.MoveTowards(vertical, frame.lift, dt * 18);
Energy = Mathf.Max(0, Energy - dt * 23);
usingEnergy = true;
}
}
Vector3 velocity = Quaternion.Euler(0, yaw, 0) * new Vector3(movement.x, 0, movement.y);
float speed = frame.speed * game.Part(2, game.Run.equipment[2]).speedFactor;
velocity = Vector3.ClampMagnitude(velocity, 1) * speed;
if (boostRemaining > 0)
{
boostRemaining -= dt;
velocity = boostDirection * frame.boostSpeed;
usingEnergy = true;
}
if (motor.isGrounded && vertical < 0)
vertical = -2;
vertical -= dt * 20;
velocity.y = vertical;
motor.Move(velocity * dt);
HorizontalSpeed = new Vector2(motor.velocity.x, motor.velocity.z).magnitude;
visual.localRotation = Quaternion.Euler(0, Mathf.LerpAngle(visual.localEulerAngles.y, yaw, dt * 9), 0);
if (!usingEnergy && Guard <= 0)
Energy = Mathf.Min(100, Energy + dt * 18);
if (transform.position.y < -20)
Damage(1000);
if (!game.MenuOpen)
{
if (!game.Action("Fire").IsPressed())
freshTrigger = false;
if (!freshTrigger && game.Action("Fire").IsPressed() && shotCooldown <= 0)
Shoot(false);
if (game.Action("Secondary").WasPressedThisFrame() && sideCooldown <= 0 && Energy >= 15)
{
Energy -= 15;
Shoot(true);
sideCooldown = 2.5f;
}
}
}
void LateUpdate()
{
if (!view)
return;
Quaternion rotation = Quaternion.Euler(pitch, yaw, 0);
Vector3 pivot = transform.position + Vector3.up * 2.7f;
Vector3 desired = FirstPerson ? pivot + rotation * new Vector3(0, 0, .25f) : pivot + rotation * new Vector3(1.25f, .25f, -6);
if (!FirstPerson)
{
Vector3 offset = desired - pivot;
if (Physics.SphereCast(pivot, .25f, offset.normalized, out var obstruction, offset.magnitude, ~(1 << 8), QueryTriggerInteraction.Ignore))
desired = pivot + offset.normalized * Mathf.Max(.5f, obstruction.distance - .15f);
}
view.transform.SetPositionAndRotation(desired, rotation);
float fov = game.MenuOpen ? 70 : game.Action("Aim").IsPressed() ? 50 : FirstPerson ? 78 : 70;
view.fieldOfView = Mathf.Lerp(view.fieldOfView, fov, Time.unscaledDeltaTime * 12);
visual.gameObject.SetActive(!FirstPerson);
cockpit.SetActive(FirstPerson);
}
void Shoot(bool secondary)
{
var part = game.Part(secondary ? 1 : 0, game.Run.equipment[secondary ? 1 : 0]);
shotCooldown = secondary ? shotCooldown : 1 / part.fireRate;
Ray ray = view.ViewportPointToRay(new Vector3(.5f, .5f));
Vector3 target = ray.GetPoint(180);
if (Physics.Raycast(ray, out var aim, 180, ~(1 << 8), QueryTriggerInteraction.Ignore))
target = aim.point;
Vector3 muzzle = transform.position + Vector3.up * 2 + Quaternion.Euler(0, yaw, 0) * new Vector3(.85f, 0, 1);
if (secondary && part.guided)
{
var enemies = FindObjectsByType<EnemyUnit>();
float nearest = 50;
foreach (var enemy in enemies)
{
float d = Vector3.Distance(muzzle, enemy.transform.position);
if (d < nearest)
{
nearest = d;
target = enemy.transform.position + Vector3.up;
}
}
}
Vector3 direction = (target - muzzle).normalized;
if (Physics.Raycast(muzzle, direction, out var hit, 180, ~(1 << 8), QueryTriggerInteraction.Ignore))
{
target = hit.point;
var enemy = hit.collider.GetComponentInParent<EnemyUnit>();
if (enemy)
{
enemy.Damage(part.damage * (1 + game.Run.upgrades * .15f));
lastHitTime = Time.time;
}
}
WorldFactory.Tracer(muzzle, target, secondary ? new Color(1, .65f, .2f) : new Color(.2f, 1, .85f));
GameAudio.Cue(secondary ? 140 : 240, .06f, .07f);
}
public bool RecentHit => Time.time - lastHitTime < .15f;
public void Damage(float amount)
{
if (!game.InField || Health <= 0)
return;
LastDamageFrame = Time.frameCount;
float armorFactor = game.Part(3, game.Run.equipment[3]).id == "reactive_guard" ? .72f : 1;
Health = Mathf.Max(0, Health - amount * armorFactor * (Guard > 0 ? .2f : 1));
game.Run.armor = Health;
}
}
}