summaryrefslogtreecommitdiff
path: root/JopaBridge.cs
diff options
context:
space:
mode:
authorprospanclaudebot <[email protected]>2026-07-10 22:10:31 +0000
committerprospanclaudebot <[email protected]>2026-07-10 22:10:31 +0000
commitf7b25e5d3d01a42c96dee90e5285706a057f6705 (patch)
tree284d4404d5797c0d759bc91dd3d3c30aac395e74 /JopaBridge.cs
downloadjopa-bridge-f7b25e5d3d01a42c96dee90e5285706a057f6705.tar.gz
jopa-bridge-f7b25e5d3d01a42c96dee90e5285706a057f6705.zip
initial import: jopa-bridgeHEADmain
Diffstat (limited to 'JopaBridge.cs')
-rw-r--r--JopaBridge.cs222
1 files changed, 222 insertions, 0 deletions
diff --git a/JopaBridge.cs b/JopaBridge.cs
new file mode 100644
index 0000000..3c4687d
--- /dev/null
+++ b/JopaBridge.cs
@@ -0,0 +1,222 @@
+using System;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Timers;
+using Terraria;
+using Terraria.ID;
+using TerrariaApi.Server;
+using TShockAPI;
+
+namespace JopaBridge
+{
+ // Мост-коллаба: TShock-сервер Terraria <-> веб-игра "Планета Жопа" (mp_server.py на :8790).
+ // События Terraria (заход/выход/босс) улетают в фид Планеты Жопа; команды /svodka /jopa тянут её статус.
+ [ApiVersion(2, 1)]
+ public class JopaBridge : TerrariaPlugin
+ {
+ public override string Name => "JopaBridge";
+ public override string Author => "Планета Жопа × Terraria";
+ public override string Description => "Мост между Terraria-сервером и веб-игрой Планета Жопа";
+ public override Version Version => new Version(1, 0, 0);
+
+ private const string JOPA = "http://127.0.0.1:8790";
+ private static readonly HttpClient Http = new HttpClient { Timeout = TimeSpan.FromSeconds(4) };
+ private Timer _svodka;
+
+ public JopaBridge(Main game) : base(game) { }
+
+ public override void Initialize()
+ {
+ ServerApi.Hooks.ServerJoin.Register(this, OnJoin);
+ ServerApi.Hooks.ServerLeave.Register(this, OnLeave);
+ ServerApi.Hooks.NpcKilled.Register(this, OnNpcKilled);
+
+ Commands.ChatCommands.Add(new Command(CmdJopa, "jopa"));
+ Commands.ChatCommands.Add(new Command(CmdSvodka, "svodka"));
+ Commands.ChatCommands.Add(new Command("jopabridge.gear", CmdGear, "gearset", "postml"));
+ Commands.ChatCommands.Add(new Command("jopabridge.build", CmdHotdog, "hotdog", "хотдог"));
+
+ _svodka = new Timer(5 * 60 * 1000) { AutoReset = true };
+ _svodka.Elapsed += (s, e) => Bcast("📡 СВОДКА: фронт Планеты Жопа держится. /jopa — связь миров · /svodka — сводка.", 120, 200, 255);
+ _svodka.Start();
+
+ TShock.Log.ConsoleInfo("[JopaBridge] Мост Планета Жопа × Terraria поднят.");
+ Push("server", "TShock-сервер JopaSMP на связи с фронтом");
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ ServerApi.Hooks.ServerJoin.Deregister(this, OnJoin);
+ ServerApi.Hooks.ServerLeave.Deregister(this, OnLeave);
+ ServerApi.Hooks.NpcKilled.Deregister(this, OnNpcKilled);
+ _svodka?.Stop(); _svodka?.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ private static string PlayerName(int who)
+ {
+ try { var p = TShock.Players[who]; return (p != null && !string.IsNullOrEmpty(p.Name)) ? p.Name : "боец"; }
+ catch { return "боец"; }
+ }
+
+ private void OnJoin(JoinEventArgs args)
+ {
+ string n = PlayerName(args.Who);
+ Bcast($"🪖 {n} десантировался на JopaSMP. Добро пожаловать на фронт.", 180, 255, 140);
+ Push("join", $"{n} зашёл на Terraria-фронт JopaSMP");
+ }
+
+ private void OnLeave(LeaveEventArgs args)
+ {
+ string n = PlayerName(args.Who);
+ Push("leave", $"{n} покинул Terraria-фронт");
+ }
+
+ private void OnNpcKilled(NpcKilledEventArgs args)
+ {
+ try
+ {
+ var npc = args.npc;
+ if (npc == null || !npc.boss) return;
+ string boss = string.IsNullOrEmpty(npc.FullName) ? "босс" : npc.FullName;
+ Bcast($"🏆 На JopaSMP повержен {boss}! Слава фронту.", 255, 180, 80);
+ Push("boss", $"Terraria: повержен босс «{boss}»");
+ }
+ catch { }
+ }
+
+ private void CmdJopa(CommandArgs args)
+ {
+ args.Player.SendMessage("🌐 Планета Жопа × Terraria — миры на связи.", 120, 200, 255);
+ args.Player.SendInfoMessage("Веб-игра (дрон-гонка/песочница) крутится на том же сервере. Твои события тут летят в её фид.");
+ args.Player.SendInfoMessage("Команды: /svodka — сводка с фронта Планеты Жопа.");
+ }
+
+ private void CmdSvodka(CommandArgs args)
+ {
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ var s = await Http.GetStringAsync(JOPA + "/health");
+ using var doc = JsonDocument.Parse(s);
+ var r = doc.RootElement;
+ int drones = r.TryGetProperty("players", out var p) ? p.GetInt32() : 0;
+ int herd = r.TryGetProperty("herd", out var h) ? h.GetInt32() : 0;
+ args.Player.SendMessage("📡 СВОДКА С ПЛАНЕТЫ ЖОПА", 120, 200, 255);
+ args.Player.SendInfoMessage($" дронов онлайн: {drones} · стадо сфинктеров: {herd} голов");
+ args.Player.SendInfoMessage($" Terraria-фронт JopaSMP: {TShock.Utils.GetActivePlayerCount()} бойцов онлайн");
+ }
+ catch
+ {
+ args.Player.SendErrorMessage("Планета Жопа не отвечает — фронт вне связи.");
+ }
+ });
+ }
+
+ // пост-Мунлорд лоадауты: (item, stack, prefix)
+ private static readonly (int id, int stack, int prefix)[] WARRIOR = {
+ (ItemID.Zenith, 1, PrefixID.Legendary),
+ (ItemID.SolarFlareHelmet, 1, 0), (ItemID.SolarFlareBreastplate, 1, 0), (ItemID.SolarFlareLeggings, 1, 0),
+ (ItemID.LongRainbowTrailWings, 1, 0), // Celestial Starboard (внутреннее имя)
+ (ItemID.AnkhShield, 1, PrefixID.Warding),
+ (ItemID.MasterNinjaGear, 1, PrefixID.Menacing),
+ (ItemID.FireGauntlet, 1, PrefixID.Menacing),
+ (ItemID.CelestialShell, 1, PrefixID.Menacing),
+ (ItemID.WarriorEmblem, 1, PrefixID.Menacing),
+ (ItemID.CharmofMyths, 1, 0),
+ (ItemID.GreaterHealingPotion, 40, 0),
+ };
+ private static readonly (int id, int stack, int prefix)[] MAGE = {
+ (ItemID.LastPrism, 1, PrefixID.Mythical),
+ (ItemID.NebulaHelmet, 1, 0), (ItemID.NebulaBreastplate, 1, 0), (ItemID.NebulaLeggings, 1, 0),
+ (ItemID.LongRainbowTrailWings, 1, 0), // Celestial Starboard (внутреннее имя)
+ (ItemID.AnkhShield, 1, PrefixID.Warding),
+ (ItemID.CelestialCuffs, 1, PrefixID.Menacing),
+ (ItemID.CelestialEmblem, 1, PrefixID.Menacing),
+ (ItemID.CelestialShell, 1, PrefixID.Menacing),
+ (ItemID.CharmofMyths, 1, 0),
+ (ItemID.SuperManaPotion, 40, 0), (ItemID.GreaterHealingPotion, 40, 0),
+ };
+
+ private void CmdHotdog(CommandArgs args)
+ {
+ int cx = Main.spawnTileX, cy = Main.spawnTileY - 34; // парит над спавном
+ const int HW = 26, HH = 11;
+ void SetTile(int wx, int wy, int type)
+ {
+ if (wx < 5 || wy < 5 || wx >= Main.maxTilesX - 5 || wy >= Main.maxTilesY - 5) return;
+ var t = Main.tile[wx, wy];
+ t.active(true); t.type = (ushort)type; t.slope(0); t.halfBrick(false);
+ }
+ for (int lx = -HW; lx <= HW; lx++)
+ for (int ly = -HH; ly <= HH; ly++)
+ {
+ double nx = (double)lx / HW, ny = (double)ly / HH; // булка (эллипс)
+ double sx = (double)lx / (HW * 1.14), sy = (double)ly / (HH * 0.34); // сосиска (тонкая, торчит по краям)
+ if (sx * sx + sy * sy <= 1.0) SetTile(cx + lx, cy + ly, TileID.FleshBlock);
+ else if (nx * nx + ny * ny <= 1.0) SetTile(cx + lx, cy + ly, TileID.Sandstone);
+ }
+ for (int lx = -HW + 2; lx <= HW - 2; lx++) // горчица-зигзаг поверх сосиски
+ {
+ double sx = (double)lx / (HW * 1.14);
+ if (sx * sx > 0.92) continue;
+ int wy = cy + (int)Math.Round(-HH * 0.12 + 2.4 * Math.Sin(lx * 0.55));
+ SetTile(cx + lx, wy, TileID.AmberGemspark);
+ SetTile(cx + lx, wy - 1, TileID.AmberGemspark);
+ }
+ int x0 = cx - HW - 1, y0 = cy - HH - 1, W = 2 * HW + 3, H = 2 * HH + 3;
+ for (int gx = x0; gx < x0 + W; gx += 40)
+ for (int gy = y0; gy < y0 + H; gy += 40)
+ TSPlayer.All.SendTileRect((short)gx, (short)gy, (byte)Math.Min(40, x0 + W - gx), (byte)Math.Min(40, y0 + H - gy));
+ args.Player.SendSuccessMessage("🌭 Гигантский хот-дог воздвигнут над спавном.");
+ Bcast("🌭 Над спавном воздвигнут ГИГАНТСКИЙ ХОТ-ДОГ волей админа. Слава фронту.", 255, 200, 80);
+ }
+
+ private void CmdGear(CommandArgs args)
+ {
+ if (args.Parameters.Count < 2)
+ {
+ args.Player.SendErrorMessage("Использование: /gearset <игрок> <mage|warrior>");
+ return;
+ }
+ var found = TSPlayer.FindByNameOrID(args.Parameters[0]);
+ if (found.Count == 0) { args.Player.SendErrorMessage("Игрок не найден."); return; }
+ if (found.Count > 1) { args.Player.SendMultipleMatchError(found.Select(p => p.Name)); return; }
+ var target = found[0];
+ string cls = args.Parameters[1].ToLowerInvariant();
+ (int id, int stack, int prefix)[] set; string label;
+ if (cls.StartsWith("mag") || cls == "маг") { set = MAGE; label = "маг · Nebula + Last Prism"; }
+ else if (cls.StartsWith("war") || cls.StartsWith("mel") || cls == "воин") { set = WARRIOR; label = "воин · Solar Flare + Zenith"; }
+ else { args.Player.SendErrorMessage("Класс: mage или warrior."); return; }
+ foreach (var it in set) target.GiveItem(it.id, it.stack, it.prefix);
+ target.SendSuccessMessage($"⚔️ Админ выдал тебе пост-Мунлорд сет: {label}.");
+ args.Player.SendSuccessMessage($"Выдал {target.Name} пост-Мунлорд сет: {label}.");
+ Bcast($"⚔️ {target.Name} экипирован пост-Мунлорд сетом ({label}) волей админа.", 255, 200, 80);
+ }
+
+ private static void Bcast(string msg, byte r, byte g, byte b)
+ {
+ try { TShock.Utils.Broadcast(msg, r, g, b); } catch { }
+ }
+
+ private static void Push(string kind, string msg)
+ {
+ _ = Task.Run(async () =>
+ {
+ try
+ {
+ string json = JsonSerializer.Serialize(new { t = "jopa_event", kind, msg });
+ await Http.PostAsync(JOPA + "/jopa_event", new StringContent(json, Encoding.UTF8, "application/json"));
+ }
+ catch { }
+ });
+ }
+ }
+}