216 lines
10 KiB
C#
216 lines
10 KiB
C#
using UnityEngine;
|
|
|
|
namespace Ironspore
|
|
{
|
|
public sealed class TrainingCourse : MonoBehaviour
|
|
{
|
|
static readonly Color Steel = new Color(.19f, .28f, .32f);
|
|
static readonly Color Amber = new Color(.95f, .48f, .12f);
|
|
static readonly Color Green = new Color(.16f, .92f, .65f);
|
|
GameSession game;
|
|
Renderer firstLamp, upperLamp;
|
|
RangeTarget plate, movingPlate;
|
|
Vector3 previousPosition;
|
|
int gateCount, hits;
|
|
float startedAt, finishSeconds;
|
|
static readonly Vector3 StartGate = new Vector3(19, 0, -15);
|
|
static readonly Vector3 UpperGate = new Vector3(19, 4.3f, 4);
|
|
static readonly Vector3 Landing = new Vector3(19, 4.3f, 22);
|
|
|
|
public string Instruction
|
|
{
|
|
get
|
|
{
|
|
Vector3 position = game.World.Player.transform.position;
|
|
if (game.World.TransportReady) return GameLocalization.Text("range.board", game.Settings.Bindings.Display("Interact"));
|
|
if (position.x < -16 && (position.z > 6 || Mathf.Abs(position.z + 8) < 4))
|
|
return GameLocalization.Text(game.Run.podUsed ? "range.recoverySent" : "range.recoveryGuide", game.Settings.Bindings.Display("Interact"), game.Settings.Bindings.Display("Inventory"), game.Settings.Bindings.Display("Cargo"));
|
|
if (position.x < -7) return GameLocalization.Text("range.targetGuide", hits);
|
|
if (gateCount == 3) return GameLocalization.Text("range.finish", finishSeconds);
|
|
if (gateCount == 2) return GameLocalization.Text("range.brake");
|
|
if (gateCount == 1) return GameLocalization.Text("range.gate");
|
|
return GameLocalization.Text("range.choose");
|
|
}
|
|
}
|
|
|
|
public void Build(GameSession session)
|
|
{
|
|
game = session;
|
|
previousPosition = game.World.Player.transform.position;
|
|
firstLamp = Gate(StartGate);
|
|
upperLamp = Gate(UpperGate);
|
|
WorldFactory.Label("range.start", StartGate + Vector3.up * 5.95f, Green, transform);
|
|
WorldFactory.Label("range.upper", UpperGate + Vector3.up * 5.95f, Green, transform);
|
|
WorldFactory.Label("range.landing", Landing + Vector3.up * 1.7f, Green, transform);
|
|
for (int i = -1; i <= 1; i++)
|
|
WorldFactory.Box("LandingStripe", Landing + new Vector3(0,.02f,i * 1.35f), new Vector3(4.7f,.035f,.17f), Amber, transform, false);
|
|
plate = Target(new Vector3(-13, 0, -2), false);
|
|
movingPlate = Target(new Vector3(-25, 0, -2), true);
|
|
WorldFactory.Box("TraverseRail", new Vector3(-25,.12f,-2), new Vector3(9,.24f,.8f), Steel, transform);
|
|
foreach (int side in new[] { -1, 1 })
|
|
WorldFactory.Box("RailStop", new Vector3(-25 + side * 4.5f,.4f,-2), new Vector3(.35f,.8f,1.3f), Amber, transform);
|
|
WorldFactory.Box("FiringLine", new Vector3(-19,.025f,-15), new Vector3(12,.04f,.15f), Amber, transform, false);
|
|
WorldFactory.Label("range.gallery", new Vector3(-19,4.6f,-2), Amber, transform);
|
|
WorldFactory.Label("range.salvage", new Vector3(-24,4,14), Green, transform);
|
|
var desk = WorldFactory.Box("RangeResetDesk", new Vector3(-12, .85f, -15), new Vector3(1.4f, 1.7f, .85f), Steel, transform);
|
|
WorldFactory.Box("ResetSwitch", desk.transform.position + Vector3.up * .92f, new Vector3(.8f,.14f,.5f), Green, transform, false, true);
|
|
var reset = desk.AddComponent<RangeReset>();
|
|
reset.kind = 4;
|
|
reset.course = this;
|
|
WorldFactory.Label("range.resetSign", desk.transform.position + Vector3.up * 1.6f, Green, transform);
|
|
}
|
|
|
|
Renderer Gate(Vector3 at)
|
|
{
|
|
foreach (int side in new[] { -1, 1 })
|
|
{
|
|
Vector3 foot = at + Vector3.right * side * 3.1f;
|
|
WorldFactory.Box("GateFoot", foot + Vector3.up * .14f, new Vector3(.8f,.28f,1.2f), Steel, transform);
|
|
WorldFactory.Box("GatePost", foot + Vector3.up * 2.65f, new Vector3(.35f,5.3f,.5f), Steel, transform);
|
|
WorldFactory.Box("GateStripe", foot + Vector3.up * 2, new Vector3(.38f,.8f,.53f), Amber, transform, false);
|
|
}
|
|
return WorldFactory.Box("GateLamp", at + Vector3.up * 5.35f, new Vector3(6.6f,.26f,.45f), Amber, transform, false, true).GetComponent<Renderer>();
|
|
}
|
|
|
|
RangeTarget Target(Vector3 at, bool moves)
|
|
{
|
|
var carriage = new GameObject(moves ? "TraversingTarget" : "FixedTarget").transform;
|
|
carriage.SetParent(transform, false);
|
|
carriage.position = at;
|
|
var stand = WorldFactory.Box("TargetStand", at + Vector3.up * .55f, new Vector3(2.2f,1.1f,.85f), Steel, carriage);
|
|
if (moves) stand.layer = 10;
|
|
var pivot = new GameObject("RangeTarget").transform;
|
|
pivot.SetParent(carriage, false);
|
|
pivot.position = at + Vector3.up * 1.15f;
|
|
var panel = WorldFactory.Box("StrikePlate", pivot.position + Vector3.up * 1.15f, new Vector3(2.1f,2.3f,.22f), Amber, pivot);
|
|
var bull = WorldFactory.Primitive(PrimitiveType.Cylinder, "Bullseye", panel.transform.position + Vector3.back * .14f, new Vector3(1.25f,.025f,1.25f), Steel, pivot, false, false);
|
|
bull.transform.rotation = Quaternion.Euler(90,0,0);
|
|
var lamp = WorldFactory.Box("TargetLamp", bull.transform.position + Vector3.back * .04f, new Vector3(.52f,.52f,.05f), Amber, pivot, false, true);
|
|
foreach (Transform part in pivot.GetComponentsInChildren<Transform>()) part.gameObject.layer = 10;
|
|
var target = pivot.gameObject.AddComponent<RangeTarget>();
|
|
target.Initialize(this, panel.GetComponent<Collider>(), lamp.GetComponent<Renderer>(), moves);
|
|
return target;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (!game.Pilot || game.Paused) return;
|
|
Vector3 position = game.Pilot.transform.position;
|
|
if ((gateCount == 0 || gateCount == 3) && Crossed(previousPosition, position, StartGate))
|
|
{
|
|
gateCount = 1;
|
|
startedAt = Time.time;
|
|
SetLamp(firstLamp, true);
|
|
SetLamp(upperLamp, false);
|
|
game.Notify("range.gate");
|
|
}
|
|
else if (gateCount == 1 && Crossed(previousPosition, position, UpperGate))
|
|
{
|
|
gateCount = 2;
|
|
SetLamp(upperLamp, true);
|
|
game.Notify("range.brake");
|
|
}
|
|
else if (gateCount == 2 && Mathf.Abs(position.x - Landing.x) < 2.65f &&
|
|
Mathf.Abs(position.z - Landing.z) < 2.4f && Mathf.Abs(position.y - Landing.y) < .6f &&
|
|
game.Pilot.Grounded && game.Pilot.HorizontalSpeed < 1)
|
|
{
|
|
gateCount = 3;
|
|
finishSeconds = Time.time - startedAt;
|
|
game.Notify("range.finish", finishSeconds);
|
|
GameAudio.Cue(620,.2f);
|
|
}
|
|
previousPosition = position;
|
|
}
|
|
|
|
static bool Crossed(Vector3 from, Vector3 to, Vector3 gate)
|
|
{
|
|
if (from.z >= gate.z || to.z < gate.z) return false;
|
|
Vector3 crossing = Vector3.Lerp(from, to, (gate.z - from.z) / (to.z - from.z));
|
|
return Mathf.Abs(crossing.x - gate.x) < 2.9f && crossing.y >= gate.y - .5f && crossing.y < gate.y + 5;
|
|
}
|
|
|
|
internal static void SetLamp(Renderer renderer, bool on)
|
|
{
|
|
renderer.sharedMaterial = WorldFactory.Material(on ? Green : Amber, true);
|
|
}
|
|
|
|
internal void Hit()
|
|
{
|
|
game.Notify("range.hit", ++hits);
|
|
GameAudio.Cue(880, .12f);
|
|
}
|
|
|
|
public void ResetPractice()
|
|
{
|
|
hits = gateCount = 0;
|
|
previousPosition = game.Pilot.transform.position;
|
|
SetLamp(firstLamp, false);
|
|
SetLamp(upperLamp, false);
|
|
plate.ResetTarget();
|
|
movingPlate.ResetTarget();
|
|
game.Notify("range.ready");
|
|
}
|
|
}
|
|
|
|
public sealed class RangeTarget : MonoBehaviour
|
|
{
|
|
TrainingCourse course;
|
|
Collider strike;
|
|
Renderer lamp;
|
|
bool down, moving;
|
|
float travelTime, holdTime;
|
|
Vector3 origin;
|
|
|
|
internal void Initialize(TrainingCourse owner, Collider collider, Renderer indicator, bool moves)
|
|
{
|
|
course = owner;
|
|
strike = collider;
|
|
lamp = indicator;
|
|
moving = moves;
|
|
origin = transform.parent.localPosition;
|
|
}
|
|
|
|
public bool Hit()
|
|
{
|
|
if (down || holdTime > 0) return false;
|
|
down = !moving;
|
|
if (moving) holdTime = 1f;
|
|
else strike.enabled = false;
|
|
TrainingCourse.SetLamp(lamp, true);
|
|
course.Hit();
|
|
return true;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (down) transform.localRotation = Quaternion.RotateTowards(transform.localRotation, Quaternion.Euler(87,0,0), 240 * Time.deltaTime);
|
|
if (!moving) return;
|
|
if (holdTime > 0)
|
|
{
|
|
holdTime -= Time.deltaTime;
|
|
if (holdTime <= 0) TrainingCourse.SetLamp(lamp, false);
|
|
return;
|
|
}
|
|
travelTime += Time.deltaTime;
|
|
transform.parent.localPosition = origin + Vector3.right * (3.1f * Mathf.Sin(travelTime * .85f));
|
|
}
|
|
|
|
internal void ResetTarget()
|
|
{
|
|
down = false;
|
|
holdTime = travelTime = 0;
|
|
transform.parent.localPosition = origin;
|
|
strike.enabled = true;
|
|
transform.localRotation = Quaternion.identity;
|
|
TrainingCourse.SetLamp(lamp, false);
|
|
}
|
|
}
|
|
|
|
public sealed class RangeReset : WorldInteractable
|
|
{
|
|
public TrainingCourse course;
|
|
public override string Prompt => GameLocalization.Text("range.reset", GameSession.Active.Settings.Bindings.Display("Interact"));
|
|
public override void Activate() => course.ResetPractice();
|
|
}
|
|
}
|