Admin: install parts, set time and weather; join without prompt
- VEHICLE: INSTALL for parts that are not installed - runs the game's own assembly (trigger "Assembly" like on load), so the part sits exactly on its pivot; synced to other players via FsmSync (MSCO_INSTALL) - WORLD: time buttons (2-hour steps via the sun FSM's load path) and CLEAR / RAIN / THUNDERSTORM (weather front moved above the admin); sent to everyone as mod message "msco.world" (no server update needed) - JOIN from the server browser/website connects right away in the main menu; server mods still ask before downloading Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -292,6 +292,7 @@ namespace MSCO
|
||||
if (Debug) MscoLog.Warning("[MSCO] << not found: " + msg.Path + " [" + msg.FsmName + "]");
|
||||
return;
|
||||
}
|
||||
if (msg.EventName == VehicleAdmin.InstallEvent) { VehicleAdmin.InstallRemote(fsm); return; } // Admin hat ein Teil eingebaut
|
||||
FsmState state = fsm.Fsm.GetState(msg.State);
|
||||
if (state == null || switchState == null) return;
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace MSCO
|
||||
/// Die Mod registriert den Link-Typ in Windows so, dass ein Klick Steam direkt mit
|
||||
/// "-applaunch 516750 +msco_connect <link>" startet - ohne cmd/Skript dazwischen, damit
|
||||
/// präparierte Links keine Befehle ausführen können. Die Mod liest die Adresse beim Start
|
||||
/// aus der Kommandozeile und fragt im Hauptmenü, ob verbunden werden soll.
|
||||
/// aus der Kommandozeile und verbindet im Hauptmenü sofort (Server-Mods fragen weiterhin nach).
|
||||
/// (Läuft das Spiel schon, gibt Steam den Link nicht weiter - dann den Server-Browser nutzen.)
|
||||
/// </summary>
|
||||
public static class JoinLinks
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
<Compile Include="PerfStats.cs" />
|
||||
<Compile Include="FsmDump.cs" />
|
||||
<Compile Include="VehicleAdmin.cs" />
|
||||
<Compile Include="WorldAdmin.cs" />
|
||||
<Compile Include="ModApi.cs" />
|
||||
<Compile Include="ModLoader.cs" />
|
||||
<Compile Include="MenuBranding.cs" />
|
||||
|
||||
+19
-1
@@ -575,6 +575,19 @@ namespace MSCO
|
||||
if (CanCheat) AnnounceCheat(what);
|
||||
}
|
||||
|
||||
/// <summary>Admin: Uhrzeit (kind 1, Stunde) oder Wetter (kind 2: 0 klar, 1 Regen, 2 Gewitter) für alle umstellen.</summary>
|
||||
public void WorldCheat(byte kind, int value)
|
||||
{
|
||||
if (!CanCheat) return;
|
||||
Vector3 pos = localPlayer != null ? localPlayer.position : Vector3.zero;
|
||||
string error = WorldAdmin.Apply(kind, value, pos);
|
||||
if (error != null) { overlays.Log(error); return; }
|
||||
SendModMessage(WorldAdmin.MessageId, WorldAdmin.Build(kind, value, pos));
|
||||
AnnounceCheat(kind == WorldAdmin.KindHour
|
||||
? "set the time to " + value.ToString("00") + ":00"
|
||||
: "set the weather to " + WorldAdmin.WeatherNames[value]);
|
||||
}
|
||||
|
||||
void AnnounceCheat(string what)
|
||||
{
|
||||
string text = OwnName() + " " + what + " (admin)";
|
||||
@@ -642,7 +655,7 @@ namespace MSCO
|
||||
PendingJoinHost = host;
|
||||
PendingJoinPort = port;
|
||||
if (IsInSession || !MenuHelper.InMenu)
|
||||
toasts.Add("Join link: " + host + ":" + port, "leave this game - then confirm JOIN in the main menu", UiTheme.Accent);
|
||||
toasts.Add("Join link: " + host + ":" + port, "leave this game - you join automatically in the main menu", UiTheme.Accent);
|
||||
}
|
||||
|
||||
public void CancelPendingJoin()
|
||||
@@ -1010,6 +1023,10 @@ namespace MSCO
|
||||
{
|
||||
CheckHitch();
|
||||
CheckJoinRequest();
|
||||
// JOIN im Server-Browser (Launcher/Website): ohne Rückfrage verbinden, sobald das Hauptmenü steht.
|
||||
// Server-Mods fragen weiterhin extra nach (das sind Programme).
|
||||
if (PendingJoinHost != null && !IsInSession && !checkingServer && MenuHelper.InMenu && Time.timeSinceLevelLoad > 1f)
|
||||
ConfirmPendingJoin();
|
||||
MeasureFps();
|
||||
// Menü-Buttons des Spiels nicht durch unser Fenster hindurch auslösen (z.B. Changelog)
|
||||
// Solange das Multiplayer-Fenster offen ist, reagiert das Spielmenü (CONTINUE, QUIT ...) gar nicht;
|
||||
@@ -1714,6 +1731,7 @@ namespace MSCO
|
||||
string modId = r.ReadString();
|
||||
int len = r.ReadInt32();
|
||||
if (len < 0 || len > MaxModMessageBytes) return;
|
||||
if (modId == WorldAdmin.MessageId) { WorldAdmin.Receive(r.ReadBytes(len)); return; } // Admin: Uhrzeit/Wetter
|
||||
ModLoader.Message(modId, from, r.ReadBytes(len));
|
||||
return;
|
||||
}
|
||||
|
||||
+37
@@ -578,9 +578,22 @@ namespace MSCO
|
||||
float right = row.xMax;
|
||||
if (RightButton(ref right, row, 66, "BREAK", danger)) { VehicleAdmin.Break(p); mp.VehicleCheat("broke " + p.Name + " (testing)"); }
|
||||
if (RightButton(ref right, row, 70, "REPAIR", secondary)) { VehicleAdmin.Repair(p); mp.VehicleCheat("repaired " + p.Name); }
|
||||
if (!installed)
|
||||
{
|
||||
// nicht eingebaut: statt der Schrauben-Knöpfe INSTALL (setzt das Teil auf seinen Halter)
|
||||
if (RightButton(ref right, row, 154, "INSTALL", primary, p.CanInstall))
|
||||
{
|
||||
string error = VehicleAdmin.Install(p);
|
||||
if (error == null) mp.VehicleCheat("installed " + p.Name);
|
||||
else Log(p.Name + ": " + error);
|
||||
}
|
||||
}
|
||||
bool bolts = p.HasBolts && installed;
|
||||
if (installed)
|
||||
{
|
||||
if (RightButton(ref right, row, 72, "LOOSEN", secondary, bolts)) { VehicleAdmin.SetBolts(p, false); mp.VehicleCheat("loosened " + p.Name); }
|
||||
if (RightButton(ref right, row, 76, "TIGHTEN", secondary, bolts)) { VehicleAdmin.SetBolts(p, true); mp.VehicleCheat("tightened " + p.Name); }
|
||||
}
|
||||
|
||||
// Verschleiß
|
||||
float wearX = row.x + 410;
|
||||
@@ -632,6 +645,30 @@ namespace MSCO
|
||||
/// <summary>Welt zurücksetzen, Backups anlegen und wiederherstellen (dedizierter Server).</summary>
|
||||
void DrawAdminWorld(MultiplayerManager mp)
|
||||
{
|
||||
// Uhrzeit: je 2 Stunden ein Knopf (so schaltet das Spiel die Tageszeit)
|
||||
int hour = WorldAdmin.CurrentHour;
|
||||
GUILayout.Label("TIME" + (hour >= 0 ? " (now " + hour.ToString("00") + ":00 - " + ((hour + 2) % 24).ToString("00") + ":00)" : ""), caption);
|
||||
Rect hours = GUILayoutUtility.GetRect(10, 30, GUILayout.ExpandWidth(true));
|
||||
float hw = (hours.width - AdminGap * 11) / 12f;
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
int h = i * 2;
|
||||
if (GUI.Button(new Rect(hours.x + i * (hw + AdminGap), hours.y, hw, 28), h.ToString("00") + ":00", hour == h ? chipOn : chipOff))
|
||||
mp.WorldCheat(WorldAdmin.KindHour, h);
|
||||
}
|
||||
GUILayout.Space(8);
|
||||
|
||||
int weatherNow = WorldAdmin.CurrentWeather;
|
||||
GUILayout.Label("WEATHER" + (weatherNow >= 0 ? " (now " + WorldAdmin.WeatherNames[weatherNow] + ")" : ""), caption);
|
||||
Rect wr = GUILayoutUtility.GetRect(10, 34, GUILayout.ExpandWidth(true));
|
||||
float ww = (wr.width - AdminGap * 2) / 3f;
|
||||
string[] labels = { "CLEAR", "RAIN", "THUNDERSTORM" };
|
||||
for (int i = 0; i < 3; i++)
|
||||
if (GUI.Button(new Rect(wr.x + i * (ww + AdminGap), wr.y, ww, 32), labels[i], weatherNow == i ? primary : secondary))
|
||||
mp.WorldCheat(WorldAdmin.KindWeather, i);
|
||||
GUILayout.Label("Rain and thunder start right above you. Everyone gets the same time and weather.", small);
|
||||
GUILayout.Space(14);
|
||||
|
||||
if (!mp.IsDedicated)
|
||||
{
|
||||
GUILayout.Label("World reset and save backups are only available on a dedicated server.", small);
|
||||
|
||||
+106
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using HutongGames.PlayMaker;
|
||||
using UnityEngine;
|
||||
@@ -30,6 +31,16 @@ namespace MSCO
|
||||
public float MaxTightness;
|
||||
public readonly List<PlayMakerFSM> Bolts = new List<PlayMakerFSM>();
|
||||
public bool HasBolts { get { return Bolts.Count > 0 || Tightness != null; } }
|
||||
// Einbau: das lose Teil (ThisPart) und der Trigger mit der FSM "Assembly" am Fahrzeug
|
||||
public FsmGameObject ThisPart, Trigger;
|
||||
public bool CanInstall
|
||||
{
|
||||
get
|
||||
{
|
||||
return Installed != null && !Installed.Value && ThisPart != null && ThisPart.Value != null
|
||||
&& Trigger != null && Trigger.Value != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly List<Fluid> fluids = new List<Fluid>();
|
||||
@@ -78,6 +89,8 @@ namespace MSCO
|
||||
Category = category.Replace("Database", "").Replace("Parts", ""),
|
||||
Damaged = damaged, Installed = installed, Wear = wear,
|
||||
Bolted = fsm.FsmVariables.FindFsmBool("Bolted"),
|
||||
ThisPart = fsm.FsmVariables.FindFsmGameObject("ThisPart"),
|
||||
Trigger = fsm.FsmVariables.FindFsmGameObject("Trigger"),
|
||||
};
|
||||
partsByDb[fsm.gameObject] = part;
|
||||
if (damaged != null || wear != null || part.Bolted != null) parts.Add(part);
|
||||
@@ -229,6 +242,99 @@ namespace MSCO
|
||||
if (p.Wear != null) p.Wear.Value = 0f;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Einbauen
|
||||
|
||||
/// <summary>
|
||||
/// Teil einbauen, wie es das Spiel beim Laden eines Spielstands macht: der Trigger am Fahrzeug bekommt das Teil
|
||||
/// (FSM "Assembly", Variable Part) und läuft durch seinen Einbau-Zustand - das setzt das Teil exakt auf seinen
|
||||
/// Halter (pivot_...), aktiviert die Schrauben und trägt es in der Database als eingebaut ein.
|
||||
/// Gibt null zurück oder den Grund, warum es nicht geht. Die anderen Spieler bauen es genauso ein.
|
||||
/// </summary>
|
||||
public static string Install(Part p)
|
||||
{
|
||||
if (!p.CanInstall) return "can't be installed";
|
||||
GameObject trigger = p.Trigger.Value, part = p.ThisPart.Value;
|
||||
if (trigger.transform.parent != null && !trigger.transform.parent.gameObject.activeInHierarchy)
|
||||
return "install the part it mounts on first";
|
||||
if (part.transform.parent != null && part.transform.parent.name == "ItemPivot") return "drop it first (it is held in a hand)";
|
||||
PlayMakerFSM assembly = Assembly(trigger);
|
||||
if (assembly == null || assembly.FsmVariables.FindFsmGameObject("Part") == null) return "no mount point found";
|
||||
if (assembly.Fsm.GetState("Check Assembly") == null && assembly.Fsm.GetState("Assemble") == null) return "no mount point found";
|
||||
|
||||
bool wasActive = trigger.activeInHierarchy;
|
||||
Run(InstallRoutine(assembly, part));
|
||||
if (wasActive) // bei den anderen Spielern genauso (der Trigger ist dort auch aktiv, solange das Teil fehlt)
|
||||
FsmSync.Outbox.Add(new FsmEventMsg
|
||||
{
|
||||
Path = FsmSync.PathOf(trigger.transform), FsmName = "Assembly", EventName = InstallEvent, State = "",
|
||||
Position = trigger.transform.position
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Event-Name der Einbau-Nachricht an die anderen Spieler (läuft über FsmSync).</summary>
|
||||
public const string InstallEvent = "MSCO_INSTALL";
|
||||
|
||||
/// <summary>Einbau-Nachricht eines Mitspielers: zum Trigger das Teil aus der Database holen und einbauen.</summary>
|
||||
public static void InstallRemote(PlayMakerFSM assembly)
|
||||
{
|
||||
FsmGameObject db = assembly.FsmVariables.FindFsmGameObject("db_ThisPart");
|
||||
if (db == null || db.Value == null) return;
|
||||
PlayMakerFSM data = null;
|
||||
foreach (PlayMakerFSM f in db.Value.GetComponents<PlayMakerFSM>()) if (f.FsmName == "Data") data = f;
|
||||
if (data == null) return;
|
||||
FsmBool installed = data.FsmVariables.FindFsmBool("Installed");
|
||||
FsmGameObject part = data.FsmVariables.FindFsmGameObject("ThisPart");
|
||||
if (installed == null || installed.Value || part == null || part.Value == null) return;
|
||||
Run(InstallRoutine(assembly, part.Value));
|
||||
}
|
||||
|
||||
static PlayMakerFSM Assembly(GameObject trigger)
|
||||
{
|
||||
foreach (PlayMakerFSM f in trigger.GetComponents<PlayMakerFSM>()) if (f.FsmName == "Assembly") return f;
|
||||
return null;
|
||||
}
|
||||
|
||||
static IEnumerator InstallRoutine(PlayMakerFSM assembly, GameObject part)
|
||||
{
|
||||
GameObject trigger = assembly.gameObject;
|
||||
bool started = trigger.activeSelf && assembly.enabled;
|
||||
part.SetActive(true);
|
||||
trigger.SetActive(true);
|
||||
assembly.enabled = true;
|
||||
if (!started) yield return null; // FSM erst starten lassen, sonst setzt ihr Start den Zustand zurück
|
||||
if (assembly == null || part == null) yield break;
|
||||
|
||||
assembly.FsmVariables.FindFsmGameObject("Part").Value = part;
|
||||
FsmBool setup = assembly.FsmVariables.FindFsmBool("Setup");
|
||||
FsmState check = assembly.Fsm.GetState("Check Assembly");
|
||||
if (setup != null && check != null)
|
||||
{
|
||||
setup.Value = true; // wie beim Laden: "Check Assembly" sieht Setup und baut über den passenden Weg ein
|
||||
FsmSync.SwitchStateSilently(assembly, check);
|
||||
}
|
||||
else FsmSync.SwitchStateSilently(assembly, assembly.Fsm.GetState("Assemble"));
|
||||
|
||||
yield return new WaitForSeconds(0.6f);
|
||||
if (part != null) SnapToPivot(part);
|
||||
Refresh(true); // Schrauben des Teils sind jetzt aktiv
|
||||
}
|
||||
|
||||
// Coroutinen ohne eigenes MonoBehaviour im Aufrufer
|
||||
sealed class Runner : MonoBehaviour { }
|
||||
static Runner runner;
|
||||
|
||||
static void Run(IEnumerator routine)
|
||||
{
|
||||
if (runner == null)
|
||||
{
|
||||
var go = new GameObject("MSCO_AdminRunner");
|
||||
UnityEngine.Object.DontDestroyOnLoad(go);
|
||||
runner = go.AddComponent<Runner>();
|
||||
}
|
||||
runner.StartCoroutine(routine);
|
||||
}
|
||||
|
||||
public static int RepairAll()
|
||||
{
|
||||
Refresh(true);
|
||||
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using HutongGames.PlayMaker;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>
|
||||
/// Admin-Menü, Reiter WORLD: Uhrzeit und Wetter umstellen. Beides läuft im Spiel über PlayMaker:
|
||||
/// - Uhr: die Sonnen-FSM hat je 2 Stunden einen Zustand ("00-02" ... "22-00") und die Zahl "Time"; beim Laden
|
||||
/// setzt das Spiel "Time" und wählt in "State 1" den passenden Zustand - genau so stellen wir die Zeit.
|
||||
/// - Wetter: die FSM "Weather" (Wolkenfront) mit den Zuständen "No weather", "Rain" und "Thunder"; bei Regen
|
||||
/// wird die Front über den Admin gesetzt, damit es dort auch regnet.
|
||||
/// Die anderen Spieler bekommen den Befehl über eine MSCO-Nachricht und führen ihn genauso aus.
|
||||
/// </summary>
|
||||
public static class WorldAdmin
|
||||
{
|
||||
public const string MessageId = "msco.world";
|
||||
public const byte KindHour = 1, KindWeather = 2;
|
||||
|
||||
public static readonly string[] WeatherStates = { "No weather", "Rain", "Thunder" };
|
||||
public static readonly string[] WeatherEvents = { "NORAIN", "RAIN", "THUNDER" };
|
||||
public static readonly string[] WeatherNames = { "clear", "rain", "thunder" };
|
||||
|
||||
static PlayMakerFSM sun, weather;
|
||||
static float searchedAt = -100f;
|
||||
|
||||
static void Find()
|
||||
{
|
||||
if (sun != null && weather != null) return;
|
||||
if (Time.realtimeSinceStartup - searchedAt < 3f) return;
|
||||
searchedAt = Time.realtimeSinceStartup;
|
||||
foreach (PlayMakerFSM f in SceneCache.Fsms(5f))
|
||||
{
|
||||
if (f == null) continue;
|
||||
if (sun == null && f.FsmVariables.FindFsmInt("Time") != null && f.Fsm.GetState("12-14") != null
|
||||
&& f.Fsm.GetState("22-00") != null && f.Fsm.GetState("State 1") != null && f.Fsm.GetState("Next day") != null)
|
||||
sun = f;
|
||||
if (weather == null && f.FsmName == "Weather" && f.Fsm.GetState("No weather") != null && f.Fsm.GetState("Rain") != null)
|
||||
weather = f;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Anzeige
|
||||
|
||||
/// <summary>Aktueller 2-Stunden-Block der Uhr (Startstunde) oder -1.</summary>
|
||||
public static int CurrentHour
|
||||
{
|
||||
get
|
||||
{
|
||||
Find();
|
||||
string s = sun != null ? sun.ActiveStateName : null;
|
||||
int h;
|
||||
if (s != null && s.Length == 5 && s[2] == '-' && int.TryParse(s.Substring(0, 2), out h)) return h;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Aktuelles Wetter (0 klar, 1 Regen, 2 Gewitter) oder -1.</summary>
|
||||
public static int CurrentWeather
|
||||
{
|
||||
get
|
||||
{
|
||||
Find();
|
||||
if (weather == null) return -1;
|
||||
FsmInt type = weather.FsmVariables.FindFsmInt("WeatherType");
|
||||
if (type == null) return -1;
|
||||
FsmState check = weather.Fsm.GetState("State 2");
|
||||
for (int i = 0; i < WeatherEvents.Length; i++)
|
||||
{
|
||||
int value;
|
||||
if (check != null && IntCompareValue(check, WeatherEvents[i], out value) && value == type.Value) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Ausführen
|
||||
|
||||
/// <summary>Befehl lokal ausführen. Gibt null zurück oder einen Fehlertext.</summary>
|
||||
public static string Apply(byte kind, int value, Vector3 position)
|
||||
{
|
||||
Find();
|
||||
try
|
||||
{
|
||||
if (kind == KindHour) return SetHour(value);
|
||||
if (kind == KindWeather) return SetWeather(value, position);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
MscoLog.Error("[MSCO] world admin: " + e);
|
||||
return "failed: " + e.Message;
|
||||
}
|
||||
return "unknown command";
|
||||
}
|
||||
|
||||
static string SetHour(int hour)
|
||||
{
|
||||
if (sun == null) return "Clock not found.";
|
||||
int start = ((hour % 24 + 24) % 24) / 2 * 2;
|
||||
string block = start.ToString("00") + "-" + ((start + 2) % 24).ToString("00");
|
||||
FsmState target = sun.Fsm.GetState(block);
|
||||
if (target == null) return "Clock state " + block + " not found.";
|
||||
|
||||
// wie beim Laden: "Time" setzen, "State 1" wählt daraus den Zustand (und stellt Licht/Farben ein)
|
||||
FsmState dispatch = sun.Fsm.GetState("State 1");
|
||||
int timeValue;
|
||||
if (dispatch != null && IntCompareValue(dispatch, block, out timeValue))
|
||||
{
|
||||
sun.FsmVariables.FindFsmInt("Time").Value = timeValue;
|
||||
FsmSync.SwitchStateSilently(sun, dispatch);
|
||||
}
|
||||
else FsmSync.SwitchStateSilently(sun, target);
|
||||
return null;
|
||||
}
|
||||
|
||||
static string SetWeather(int kind, Vector3 position)
|
||||
{
|
||||
if (weather == null) return "Weather not found.";
|
||||
if (kind < 0 || kind >= WeatherStates.Length) return "Unknown weather.";
|
||||
FsmState state = weather.Fsm.GetState(WeatherStates[kind]);
|
||||
if (state == null) return "Weather state not found.";
|
||||
FsmSync.SwitchStateSilently(weather, state);
|
||||
if (kind > 0) MoveFrontOver(position);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wolkenfront über eine Stelle setzen: "Move clouds" setzt jedes Bild die Position aus PosX/PosZ - also diese
|
||||
/// beiden so wählen, dass die Front über der Stelle steht (Koordinatenraum aus der SetPosition-Aktion).
|
||||
/// </summary>
|
||||
static void MoveFrontOver(Vector3 position)
|
||||
{
|
||||
FsmState move = weather.Fsm.GetState("Move clouds");
|
||||
if (move == null) return;
|
||||
if (!move.ActionsLoaded) move.LoadActions();
|
||||
if (move.Actions == null) return;
|
||||
foreach (FsmStateAction a in move.Actions)
|
||||
{
|
||||
if (a == null || a.GetType().Name != "SetPosition") continue;
|
||||
Type t = a.GetType();
|
||||
var owner = Field(t, a, "gameObject") as FsmOwnerDefault;
|
||||
var x = Field(t, a, "x") as FsmFloat;
|
||||
var z = Field(t, a, "z") as FsmFloat;
|
||||
object space = Field(t, a, "space");
|
||||
GameObject go = owner != null ? weather.Fsm.GetOwnerDefaultTarget(owner) : null;
|
||||
if (go == null) continue;
|
||||
|
||||
bool local = space is Space && (Space)space == Space.Self && go.transform.parent != null;
|
||||
Vector3 p = local ? go.transform.parent.InverseTransformPoint(position) : position;
|
||||
SetVar(x, p.x);
|
||||
SetVar(z, p.z);
|
||||
go.transform.position = new Vector3(position.x, go.transform.position.y, position.z);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetVar(FsmFloat field, float value)
|
||||
{
|
||||
if (field == null || field.IsNone || string.IsNullOrEmpty(field.Name)) return;
|
||||
FsmFloat v = weather.FsmVariables.FindFsmFloat(field.Name);
|
||||
if (v != null) v.Value = value;
|
||||
}
|
||||
|
||||
static object Field(Type t, object o, string name)
|
||||
{
|
||||
var f = t.GetField(name);
|
||||
return f != null ? f.GetValue(o) : null;
|
||||
}
|
||||
|
||||
/// <summary>In einem Verteiler-Zustand die Zahl finden, bei der IntCompare das Event auslöst ("gleich").</summary>
|
||||
static bool IntCompareValue(FsmState state, string eventName, out int value)
|
||||
{
|
||||
value = 0;
|
||||
if (!state.ActionsLoaded) state.LoadActions();
|
||||
if (state.Actions == null) return false;
|
||||
foreach (FsmStateAction a in state.Actions)
|
||||
{
|
||||
if (a == null || a.GetType().Name != "IntCompare") continue;
|
||||
Type t = a.GetType();
|
||||
var equal = Field(t, a, "equal") as FsmEvent;
|
||||
var number = Field(t, a, "integer2") as FsmInt;
|
||||
if (equal == null || number == null || equal.Name != eventName) continue;
|
||||
value = number.Value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- Netz
|
||||
|
||||
public static byte[] Build(byte kind, int value, Vector3 position)
|
||||
{
|
||||
using (var ms = new MemoryStream())
|
||||
using (var w = new BinaryWriter(ms))
|
||||
{
|
||||
w.Write(kind); w.Write(value);
|
||||
w.Write(position.x); w.Write(position.y); w.Write(position.z);
|
||||
w.Flush();
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Receive(byte[] data)
|
||||
{
|
||||
if (!MenuHelper.InGame || data == null || data.Length < 17) return;
|
||||
using (var r = new BinaryReader(new MemoryStream(data)))
|
||||
{
|
||||
byte kind = r.ReadByte();
|
||||
int value = r.ReadInt32();
|
||||
var pos = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
|
||||
string error = Apply(kind, value, pos);
|
||||
if (error != null) MscoLog.Warning("[MSCO] world admin from other player: " + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user