222 lines
10 KiB
C#
222 lines
10 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using HutongGames.PlayMaker;
|
|
using MSCO;
|
|
using UnityEngine;
|
|
|
|
[assembly: AssemblyTitle("Show Bolt Size")]
|
|
[assembly: AssemblyProduct("Show Bolt Size")]
|
|
[assembly: AssemblyDescription("Shows the size of the bolt you look at, so you pick the right wrench.")]
|
|
[assembly: AssemblyVersion("1.0.3.0")]
|
|
[assembly: AssemblyFileVersion("1.0.3.0")]
|
|
[assembly: AssemblyInformationalVersion("1.0.3")]
|
|
|
|
namespace ShowBoltSize
|
|
{
|
|
/// <summary>
|
|
/// Zeigt unter dem Fadenkreuz die Größe der Schraube, auf die man schaut ("10 mm"). Jede Schraube hat die FSM "Screw"
|
|
/// mit der Variable "BoltSize". Deren Kollider sind nur mit Werkzeug in der Hand aktiv - deshalb kein Raycast, sondern:
|
|
/// die Schraube, die am nächsten an der Blickrichtung liegt (bis 1,3 m). Nur Anzeige, ändert nichts am Spiel.
|
|
/// </summary>
|
|
public class ShowBoltSize : MscoMod
|
|
{
|
|
public override string ID { get { return "showboltsize"; } }
|
|
public override string Name { get { return "Show Bolt Size"; } }
|
|
public override string Version { get { return "1.0.3"; } }
|
|
public override string Author { get { return "MBM"; } }
|
|
|
|
const float MaxDistance = 1.5f;
|
|
const float AimTolerance = 0.05f; // so weit (m) darf die Schraube neben der Blicklinie liegen
|
|
const float ScanInterval = 5f;
|
|
|
|
sealed class Bolt
|
|
{
|
|
public Transform Transform;
|
|
public Renderer Renderer; // sichtbarer Schraubenkopf (Kind-Objekt) - sein Mittelpunkt statt des Drehpunkts
|
|
public FsmFloat SizeF;
|
|
public FsmInt SizeI;
|
|
}
|
|
|
|
readonly List<Bolt> bolts = new List<Bolt>();
|
|
bool inGame;
|
|
float nextScan;
|
|
string label;
|
|
GUIStyle style;
|
|
Camera camera;
|
|
bool reported;
|
|
int diagnostics; // die ersten Male ins Log schreiben, was in Blickrichtung liegt
|
|
float nextDiagnostic;
|
|
bool shownOnce;
|
|
|
|
// Das Spiel selbst: Schraubenschlüssel/Ratsche in der Hand -> FPSCamera/2Spanner/Raycast, FSM "Check" mit "BoltSize"
|
|
// (die Größe der Schraube, auf die das Werkzeug gerade zielt). Die Variable "BoltSize" der Schrauben ist oft 0.
|
|
PlayMakerFSM toolCheck;
|
|
FsmFloat toolSize;
|
|
FsmInt toolSizeI;
|
|
float lastToolSize = -1f;
|
|
int toolLogs;
|
|
bool dumped;
|
|
float dumpAt = -1f;
|
|
|
|
public override void OnMenuLoad() { inGame = false; bolts.Clear(); label = null; camera = null; reported = false; toolCheck = null; toolSize = null; toolSizeI = null; }
|
|
public override void OnGameLoad() { inGame = true; nextScan = 0f; }
|
|
|
|
/// <summary>
|
|
/// Spielerkamera: Camera.main gibt es in My Summer Car nicht immer (die Kamera des Spielers ist "FPSCamera").
|
|
/// </summary>
|
|
Camera FindCamera()
|
|
{
|
|
if (camera != null && camera.enabled && camera.gameObject.activeInHierarchy) return camera;
|
|
camera = null;
|
|
foreach (Camera c in Camera.allCameras)
|
|
if (c.name == "FPSCamera") { camera = c; break; }
|
|
if (camera == null) camera = Camera.main;
|
|
return camera;
|
|
}
|
|
|
|
static Vector3 Center(Bolt b) { return b.Renderer != null ? b.Renderer.bounds.center : b.Transform.position; }
|
|
|
|
public override void Update()
|
|
{
|
|
label = null;
|
|
if (!inGame) return;
|
|
Camera cam = FindCamera();
|
|
if (cam == null) return;
|
|
if (Time.realtimeSinceStartup >= nextScan) Scan(cam);
|
|
float fromTool = ToolSize(cam);
|
|
|
|
// Die Schraube, die am nächsten an der Blicklinie liegt (Abstand zur Linie, nicht Winkel)
|
|
Transform eye = cam.transform;
|
|
Vector3 eyePos = eye.position, forward = eye.forward;
|
|
Bolt best = null, nearest = null;
|
|
float bestOff = float.MaxValue, nearestOff = float.MaxValue;
|
|
for (int i = 0; i < bolts.Count; i++)
|
|
{
|
|
Bolt b = bolts[i];
|
|
if (b.Transform == null || !b.Transform.gameObject.activeInHierarchy) continue;
|
|
Vector3 to = Center(b) - eyePos;
|
|
float along = Vector3.Dot(to, forward);
|
|
if (along < 0.05f || along > MaxDistance + 0.5f) continue;
|
|
float off = Vector3.Cross(forward, to).magnitude; // Abstand zur Blicklinie
|
|
if (off < nearestOff) { nearestOff = off; nearest = b; }
|
|
if (along <= MaxDistance && off < AimTolerance && off < bestOff) { bestOff = off; best = b; }
|
|
}
|
|
if (fromTool > 0f)
|
|
{
|
|
label = Format(fromTool); // was das Spiel für das Werkzeug ermittelt - immer richtig
|
|
}
|
|
else if (best != null)
|
|
{
|
|
label = Describe(best);
|
|
if (!shownOnce) { shownOnce = true; Log("first bolt: " + Info(best, eyePos, forward) + " -> " + label); }
|
|
}
|
|
else if (nearest != null && diagnostics < 8 && Time.realtimeSinceStartup >= nextDiagnostic)
|
|
{
|
|
// Hilft bei der Fehlersuche: was liegt in Blickrichtung, warum wird nichts angezeigt?
|
|
nextDiagnostic = Time.realtimeSinceStartup + 4f;
|
|
if (nearestOff < 0.3f) { diagnostics++; Log("nearest bolt (not shown): " + Info(nearest, eyePos, forward)); }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Größe aus dem Werkzeug des Spielers (nur mit Schraubenschlüssel/Ratsche in der Hand), sonst 0. Beim ersten Mal
|
|
/// wird außerdem einmal die Werkzeug-FSM in MSCOsmdump.txt geschrieben (Fehlersuche).
|
|
/// </summary>
|
|
float ToolSize(Camera cam)
|
|
{
|
|
if (toolCheck == null)
|
|
{
|
|
Transform raycast = cam.transform.Find("2Spanner/Raycast");
|
|
if (raycast == null) return 0f;
|
|
foreach (PlayMakerFSM f in raycast.GetComponents<PlayMakerFSM>())
|
|
if (f.FsmName == "Check") { toolCheck = f; break; }
|
|
if (toolCheck == null) return 0f;
|
|
toolSize = toolCheck.FsmVariables.FindFsmFloat("BoltSize");
|
|
toolSizeI = toolCheck.FsmVariables.FindFsmInt("BoltSize");
|
|
Log("tool found: " + toolCheck.name + "/" + toolCheck.FsmName + (toolSize != null ? " with float BoltSize" : toolSizeI != null ? " with int BoltSize" : " without BoltSize"));
|
|
}
|
|
if (!toolCheck.gameObject.activeInHierarchy || !toolCheck.enabled) return 0f;
|
|
if (!dumped)
|
|
{
|
|
if (dumpAt < 0f) dumpAt = Time.realtimeSinceStartup + 2f; // erst laufen lassen, dann sind die Aktionen geladen
|
|
else if (Time.realtimeSinceStartup >= dumpAt)
|
|
{
|
|
dumped = true;
|
|
try { Log("tool dump: " + FsmDump.Write("2spanner#0|raycast#0")); }
|
|
catch (System.Exception e) { Log("tool dump failed: " + e.Message); }
|
|
}
|
|
}
|
|
float value = toolSize != null ? toolSize.Value : toolSizeI != null ? toolSizeI.Value : 0f;
|
|
if (value != lastToolSize && toolLogs < 12)
|
|
{
|
|
lastToolSize = value;
|
|
toolLogs++;
|
|
Log("tool BoltSize=" + value + " (state " + toolCheck.ActiveStateName + ")");
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static string Info(Bolt b, Vector3 eyePos, Vector3 forward)
|
|
{
|
|
Vector3 to = Center(b) - eyePos;
|
|
float raw = b.SizeF != null ? b.SizeF.Value : (b.SizeI != null ? b.SizeI.Value : -1);
|
|
return b.Transform.name + " under " + (b.Transform.parent != null ? b.Transform.parent.name : "-")
|
|
+ ", distance " + Vector3.Dot(to, forward).ToString("0.00") + " m, off the line " + (Vector3.Cross(forward, to).magnitude * 100f).ToString("0") + " cm"
|
|
+ ", angle " + Vector3.Angle(forward, to).ToString("0.0") + "°, BoltSize=" + raw + (b.Renderer != null ? "" : " (no renderer)");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schrauben aus MSCOs gemeinsamer Liste aller FSMs holen (SceneCache) - nicht selbst das ganze Spiel durchsuchen,
|
|
/// das ließ das Spiel alle paar Sekunden kurz hängen.
|
|
/// </summary>
|
|
void Scan(Camera cam)
|
|
{
|
|
nextScan = Time.realtimeSinceStartup + ScanInterval;
|
|
bolts.Clear();
|
|
foreach (PlayMakerFSM fsm in SceneCache.Fsms(30f))
|
|
{
|
|
if (fsm == null || fsm.FsmName != "Screw") continue;
|
|
FsmFloat f = fsm.FsmVariables.FindFsmFloat("BoltSize");
|
|
FsmInt i = fsm.FsmVariables.FindFsmInt("BoltSize");
|
|
if (f == null && i == null) continue;
|
|
bolts.Add(new Bolt { Transform = fsm.transform, SizeF = f, SizeI = i, Renderer = fsm.GetComponentInChildren<Renderer>() });
|
|
}
|
|
if (!reported)
|
|
{
|
|
reported = true;
|
|
Log("camera " + cam.name + ", " + bolts.Count + " bolts with a size");
|
|
}
|
|
}
|
|
|
|
/// <summary>Größe in mm. Das Spiel speichert sie je nach Schraube als 10 oder als 1.0 (= 10 mm).</summary>
|
|
static string Describe(Bolt b)
|
|
{
|
|
float value = b.SizeF != null ? b.SizeF.Value : b.SizeI.Value;
|
|
if (value <= 0f) return "? mm"; // Größe unbekannt - aber die Schraube wurde erkannt
|
|
return Format(value);
|
|
}
|
|
|
|
static string Format(float value)
|
|
{
|
|
float mm = value < 2.5f ? value * 10f : value;
|
|
return Mathf.RoundToInt(mm) + " mm";
|
|
}
|
|
|
|
public override void OnGUI()
|
|
{
|
|
if (label == null) return;
|
|
if (style == null)
|
|
{
|
|
style = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontSize = 16, fontStyle = FontStyle.Bold };
|
|
style.normal.textColor = Color.white;
|
|
}
|
|
var r = new Rect(Screen.width / 2f - 50f, Screen.height / 2f + 22f, 100f, 26f);
|
|
Color old = GUI.color;
|
|
GUI.color = new Color(0f, 0f, 0f, 0.6f);
|
|
GUI.DrawTexture(r, Texture2D.whiteTexture);
|
|
GUI.color = old;
|
|
GUI.Label(r, label, style);
|
|
}
|
|
}
|
|
}
|