champions-of-luneraria/Assets/Luneraria/Core/Net/NetModel.cs
Martin Stickelbroeck 8e4b52f4ab S1 Strategie-Regeln nach Legion TD 2, Dota 2 und League of Legends
Hinter MatchOptions.Extended (Standard im Spiel, Schalter im Startbildschirm):
Söldner-Einkommen (Angriffschampion +Preis/12 Einkommen dauerhaft), Kasernen
(gefallener innerer Turm: Gegnerwellen +25 % und ein Monster mehr), Erschöpfung
und Rückkauf (60 % Leben, Rückkauf 60 + 20 x Stufe), Mondsegen (Grottensieg
+4 % je Stapel, Mondseele bei 4). Bots wählen mit erweiterten Regeln auch
Sylvara und kaufen Helden zurück. Netz überträgt Regeln und neue Zustände.
HUD: Einkommen, Kasernen-Symbol, Rückkauf, Segen im Teamkopf.
Kern 531/531 (s1 31), EditMode 31/31, PlayMode 16/16, Verify 718/0,
Selbsttest 30/30, Golden 0 Abweichungen, Fingerabdruck unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:18:47 +02:00

124 lines
4.4 KiB
C#

using System.Collections.Generic;
namespace Luneraria.Core
{
/// <summary>Roles of a session (lune_session.gd Role).</summary>
public enum LuneRole { Offline, Host, Client }
/// <summary>One lobby row: peer 0 is a bot, peer 1 the host.</summary>
public sealed class LobbyEntry
{
public int Peer;
public int Slot;
public string Name = "";
public bool Ready;
public bool Bot;
public LobbyEntry Clone() { return (LobbyEntry)MemberwiseClone(); }
}
/// <summary>Result of a session intent (Godot returns a Dictionary with ok, message, pending).</summary>
public struct SessionResult
{
public bool Ok;
public string Message;
public bool Pending;
public SessionResult(bool ok, string message, bool pending = false)
{
Ok = ok;
Message = message;
Pending = pending;
}
}
/// <summary>One player row of the public state; Draft is set only for the receiving seat.</summary>
public sealed class NetPlayerRow
{
public int Slot;
public string Name = "";
public bool IsHuman;
public int Gold;
public bool Ready;
public int Earned;
public int Spent;
public int NextSeq;
public List<RosterEntry> Roster = new List<RosterEntry>();
public HeroState Hero;
public List<Booking> Bookings = new List<Booking>();
/// <summary>Private draft of the receiving seat only; null for every other seat.</summary>
public Draft Draft;
/// <summary>Undo stack size of the receiving seat (0 for others).</summary>
public int Undo;
/// <summary>S1: permanent champion income (public, like the champion bookings).</summary>
public int IncomeBonus;
}
/// <summary>Public state of the match for one seat (rpc_state).</summary>
public sealed class NetState
{
public Phase Phase;
public int Round;
public int Winner;
public string EndNote = "";
public Dictionary<string, double> BuildingHp = new Dictionary<string, double>();
public BossState Boss = new BossState();
public Settlement LastSettlement;
public List<int> DestroyedCores = new List<int>();
public List<NetPlayerRow> Players = new List<NetPlayerRow>();
}
/// <summary>A mobile unit as it crosses the wire (rpc_combat_begin / rpc_units_spawned).</summary>
public sealed class NetUnitRecord
{
public int Id;
public string Kind = "";
public int Team;
public int TeamIndex;
public int OwnerSlot = -1;
public string Uid = "";
public string Lane = "";
public string Zone = "";
public UnitDef Def = new UnitDef();
public double X;
public double Y;
public double Facing;
public double Hp;
public double MaxHp;
public bool Alive;
}
/// <summary>
/// Outbound session messages (the Godot RPCs). A transport or a test recorder implements this;
/// without a link the session still runs offline and for in-process host tests.
/// </summary>
public interface ILuneNetLink
{
/// <summary>Reliable message to one peer (1 = host).</summary>
void Send(int peer, string kind, object payload);
/// <summary>Unreliable ordered combat frame to one peer.</summary>
void SendFrame(int peer, int serial, double elapsed, float[] units, float[] buildingHp);
}
/// <summary>Frames carry 13 floats per mobile unit (lune_session.gd FRAME_STRIDE).</summary>
public static class LuneNet
{
public const int DefaultPort = 24571;
public const int MaxClients = 5;
public const int Protocol = 4;
/// <summary>Seconds between combat snapshots the host sends.</summary>
public const double FrameInterval = 0.05;
public const int FrameStride = 13;
public const string KHello = "hello";
public const string KSubmit = "submit";
public const string KReady = "ready";
public const string KRefused = "refused";
public const string KFeedback = "feedback";
public const string KSetup = "setup";
public const string KState = "state";
public const string KLobby = "lobby";
public const string KCombatBegin = "combat_begin";
public const string KUnitsSpawned = "units_spawned";
}
}