using Newtonsoft.Json; using System; using System.Collections.Generic; using Oxide.Core.Plugins; using UnityEngine; /* * MajestatBuild 1.0.0 * ==================== * Moduł dla MajestatCore 3.2.0 — zlicza postawione konstrukcje. * * WYMAGA: MajestatCore 3.2.0+ * * Rejestruje zakładkę "BUILD" w UI MajestatCore przez API_RegisterTab. * * Śledzone statystyki: * Build.Total — wszystkie postawione obiekty łącznie * Build.Walls — ściany, drzwi, okna, ramy * Build.Floors — podłogi, schody, skośne podłogi * Build.Foundation — fundamenty (foundation, triangle.foundation) * Build.Roof — dachy (roof, roof.triangle, roof.flat) * Build.Turrets — wieżyczki automatyczne (autoturret) * Build.Traps — pułapki (beartrap, landmine, shotguntrap, flameturret) * Build.Doors — drzwi * Build.Electrical — elementy elektryczne (solary, baterie, switche) * Build.Pipes — rury wodne i przemysłowe */ namespace Oxide.Plugins { [Info("MajestatTopBuild", "Wo0t", "1.0.2")] [Description("Build module for MajestatTopCore — tracks placed structures.")] class MajestatTopBuild : RustPlugin { [PluginReference] Plugin MajestatTopCore; private bool _registered = false; // Poziom logowania pobierany z Core (0=all 1=system 2=alert 3=none) private int _logLevel = 1; private void MLog(int level, string msg) { int effective = MajestatTopCore != null ? (int)(MajestatTopCore.Call("API_GetLogLevel") ?? 1) : _logLevel; if (level < effective) return; // Podczas AutoReload wycisz komunikaty systemowe (1) — Core drukuje summary if (level == 1 && MajestatTopCore != null) { var ar = MajestatTopCore.Call("API_IsAutoReloading"); if (ar is bool b && b) return; } if (level >= 2) PrintWarning(msg); else Puts(msg); } // ── Update checker ──────────────────────────────────────────── private const string PluginVersion = "1.0.2"; private static readonly string PluginUpdateUrl = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String("aHR0cDovL2Rvd25sb2FkLmtvbGRyaXguY29tL3J1c3QvcGx1Z2lucy9NYWplc3RhdFRvcEJ1aWxkL3ZlcnNpb24uanNvbg==")); // ═════════════════════════════════════════════════════════════ // INIT // ═════════════════════════════════════════════════════════════ void OnServerInitialized() { if (MajestatTopCore == null) { PrintWarning("[MajestatTopBuild] MajestatTopCore is not loaded! Module inactive."); return; } if (!CheckMinVersion(MajestatTopCore.Version, 3, 2, 0)) { PrintError($"[MajestatTopBuild] Requires MajestatTopCore 3.2.0+. Installed: {MajestatTopCore.Version}. Module inactive."); return; } RegisterTab(); } private static bool CheckMinVersion(Oxide.Core.VersionNumber v, int major, int minor, int patch) => v.Major > major || (v.Major == major && (v.Minor > minor || (v.Minor == minor && v.Patch >= patch))); void OnPluginLoaded(Plugin plugin) { if (plugin?.Name == "MajestatTopCore" && !_registered) { if (!CheckMinVersion(plugin.Version, 3, 2, 0)) { PrintError($"[MajestatTopBuild] Requires MajestatTopCore 3.2.0+. Installed: {plugin.Version}."); return; } RegisterTab(); } } void OnPluginUnloaded(Plugin plugin) { if (plugin?.Name == "MajestatTopCore") _registered = false; } void Unload() { if (MajestatTopCore != null && _registered) MajestatTopCore.Call("API_UnregisterTab", "build"); } // ═════════════════════════════════════════════════════════════ // REJESTRACJA ZAKŁADKI // ═════════════════════════════════════════════════════════════ private void RegisterTab() { if (MajestatTopCore == null) return; var columns = new List> { new Dictionary { ["key"] = "Build.Total", ["header"] = "TOTAL", ["lang_key"] = "Build.Total" }, new Dictionary { ["key"] = "Build.Walls", ["header"] = "ŚCIANY", ["lang_key"] = "Build.Walls" }, new Dictionary { ["key"] = "Build.Foundation", ["header"] = "FUNDAM.", ["lang_key"] = "Build.Foundation" }, new Dictionary { ["key"] = "Build.Floors", ["header"] = "PODŁOGI", ["lang_key"] = "Build.Floors" }, new Dictionary { ["key"] = "Build.Roof", ["header"] = "DACH", ["lang_key"] = "Build.Roof" }, new Dictionary { ["key"] = "Build.Doors", ["header"] = "DRZWI", ["lang_key"] = "Build.Doors" }, new Dictionary { ["key"] = "Build.Turrets", ["header"] = "WIEŻYCZKI", ["lang_key"] = "Build.Turrets" }, new Dictionary { ["key"] = "Build.Traps", ["header"] = "PUŁAPKI", ["lang_key"] = "Build.Traps" }, new Dictionary { ["key"] = "Build.Electrical", ["header"] = "ELEKTR.", ["lang_key"] = "Build.Electrical" }, new Dictionary { ["key"] = "Build.Pipes", ["header"] = "RURY", ["lang_key"] = "Build.Pipes" }, }; var tabDef = new Dictionary { ["id"] = "build", ["label"] = "BUILD", ["sort"] = "Build.Total", ["fame_sort"] = "Build.Total", ["order"] = 20, // TOP PvP=0, Gather=10, Build=20 ["columns"] = columns }; // Zarejestruj tłumaczenia kolumn lang.RegisterMessages(new Dictionary { ["Build.Total"] = "TOTAL", ["Build.Walls"] = "WALLS", ["Build.Foundation"] = "FOUND.", ["Build.Floors"] = "FLOORS", ["Build.Roof"] = "ROOF", ["Build.Doors"] = "DOORS", ["Build.Turrets"] = "TURRETS", ["Build.Traps"] = "TRAPS", ["Build.Electrical"] = "ELECTR.", ["Build.Pipes"] = "PIPES", }, this); lang.RegisterMessages(new Dictionary { ["Build.Total"] = "TOTAL", ["Build.Walls"] = "ŚCIANY", ["Build.Foundation"] = "FUNDAM.", ["Build.Floors"] = "PODŁOGI", ["Build.Roof"] = "DACH", ["Build.Doors"] = "DRZWI", ["Build.Turrets"] = "WIEŻYCZKI", ["Build.Traps"] = "PUŁAPKI", ["Build.Electrical"] = "ELEKTR.", ["Build.Pipes"] = "RURY", }, this, "pl"); bool ok = (bool)(MajestatTopCore.Call("API_RegisterTab", tabDef) ?? false); if (!ok) { PrintError("Tab registration failed."); return; } MajestatTopCore.Call("API_RegisterXpSource", "Build.Walls", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Foundation", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Floors", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Roof", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Doors", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Turrets", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Traps", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Electrical", 0.0); MajestatTopCore.Call("API_RegisterXpSource", "Build.Pipes", 0.0); _registered = true; MajestatTopCore.Call("API_RegisterModule", new Dictionary { ["name"] = "MajestatTopBuild", ["version"] = PluginVersion, ["author"] = "Wo0t", ["description"] = "Build stats module" }); // startup log handled by Core } // ═════════════════════════════════════════════════════════════ // HOOK — POSTAWIENIE OBIEKTU // ═════════════════════════════════════════════════════════════ void OnEntityBuilt(Planner planner, GameObject go) { if (planner == null || go == null || MajestatTopCore == null) return; var player = planner.GetOwnerPlayer(); if (player == null) return; var entity = go.GetComponent(); if (entity == null) return; string prefab = entity.ShortPrefabName?.ToLower() ?? ""; string statKey = ResolveKey(prefab); if (statKey == null) return; MajestatTopCore.Call("API_AddStat", player.UserIDString, statKey, 1.0, true, true); MajestatTopCore.Call("API_AddStat", player.UserIDString, "Build.Total", 1.0, true, false); MLog(0, $"BUILD: {player.displayName} placed {prefab} -> {statKey}"); } // ═════════════════════════════════════════════════════════════ // MAPOWANIE PREFAB → STATYSTYKA // ═════════════════════════════════════════════════════════════ private string ResolveKey(string prefab) { if (prefab == null) return null; // Ściany (wall, doorway, window, embrasure, wall.frame) if (prefab.Contains("wall") && !prefab.Contains("turret") && !prefab.Contains("fireplace")) return "Build.Walls"; // Fundamenty (foundation, triangle.foundation) if (prefab.Contains("foundation")) return "Build.Foundation"; // Dachy (roof, roof.triangle, roof.flat) if (prefab.Contains("roof")) return "Build.Roof"; // Podłogi i schody (floor, stairs, ramp) if (prefab.Contains("floor") || prefab.Contains("stairs") || prefab.Contains("ramp")) return "Build.Floors"; // Drzwi (door, hatch, shutter) if (prefab.Contains("door") || prefab.Contains("hatch") || prefab.Contains("shutter")) return "Build.Doors"; // Wieżyczki automatyczne if (prefab.Contains("autoturret") || prefab.Contains("sam.site")) return "Build.Turrets"; // Pułapki (beartrap, landmine, shotguntrap, flameturret, guntrap) if (prefab.Contains("beartrap") || prefab.Contains("landmine") || prefab.Contains("shotguntrap") || prefab.Contains("flameturret") || prefab.Contains("guntrap") || prefab.Contains("spikes")) return "Build.Traps"; // Elektryka (solarpanel, battery, switch, generator, splitter, // combiner, blocker, xor, timer, counter, pressurepad, // sirenlight, strobelight, smartalarm, rf.broadcaster) if (prefab.Contains("electric") || prefab.Contains("solar") || prefab.Contains("battery") || prefab.Contains("switch") || prefab.Contains("generator") || prefab.Contains("splitter") || prefab.Contains("combiner") || prefab.Contains("blocker") || prefab.Contains("timer") || prefab.Contains("counter") || prefab.Contains("pressurepad") || prefab.Contains("sirenlight") || prefab.Contains("strobelight") || prefab.Contains("smartalarm") || prefab.Contains("rf.broadcaster") || prefab.Contains("rf.receiver") || prefab.Contains("igniter") || prefab.Contains("doorcontroller")) return "Build.Electrical"; // Rury i przemysł (water.pump, fluid.combiner, fluid.splitter, // industrial.conveyor, industrial.crafter) if (prefab.Contains("water.pump") || prefab.Contains("fluid") || prefab.Contains("industrial") || prefab.Contains("pipe") || prefab.Contains("sprinkler") || prefab.Contains("water.catcher")) return "Build.Pipes"; return null; } // ═════════════════════════════════════════════════════════════ // UPDATE CHECKER — synchronizowany przez MajestatTopCore // Core wywołuje hook OnMajestatUpdateCheck na wszystkich modułach // ═════════════════════════════════════════════════════════════ void OnMajestatUpdateCheck() { webrequest.Enqueue(PluginUpdateUrl, null, OnUpdateResponse, this); } private void OnUpdateResponse(int code, string response) { string pluginName = "MajestatTopBuild"; if (code == 0 || code >= 400 || string.IsNullOrEmpty(response)) { MLog(2, $"[Update] Update check failed - server unavailable (HTTP {code})."); MajestatTopCore?.Call("API_ReportUpdateResult", pluginName, null, null); return; } try { var data = Newtonsoft.Json.JsonConvert.DeserializeObject>(response); string latest = data != null && data.ContainsKey("version") ? data["version"].Trim() : null; if (latest != null && IsNewerVersion(latest, PluginVersion)) { string url = data.ContainsKey("url") ? data["url"] : PluginUpdateUrl; PrintWarning($"[Update] Newer version available ({latest}) - your version ({PluginVersion})"); MajestatTopCore?.Call("API_ReportUpdateResult", pluginName, latest, url); } else { MLog(1, $"[Update] Up to date ({PluginVersion}) - no updates."); MajestatTopCore?.Call("API_ReportUpdateResult", pluginName, null, null); } } catch (Exception ex) { MLog(2, $"[Update] Error: {ex.Message}"); MajestatTopCore?.Call("API_ReportUpdateResult", pluginName, null, null); } } private bool IsNewerVersion(string latest, string current) { try { // Odetnij sufiks wersji RC (np. "1.0.0-310520262150" → "1.0.0") string lc = latest != null && latest.Contains("-") ? latest.Substring(0, latest.IndexOf('-')) : latest; string cc = current != null && current.Contains("-") ? current.Substring(0, current.IndexOf('-')) : current; var l = System.Array.ConvertAll(lc.Split('.'), int.Parse); var c = System.Array.ConvertAll(cc.Split('.'), int.Parse); int len = System.Math.Max(l.Length, c.Length); for (int i = 0; i < len; i++) { int lv = i < l.Length ? l[i] : 0; int cv = i < c.Length ? c[i] : 0; if (lv > cv) return true; if (lv < cv) return false; } return false; } catch { return false; } } } }