using System.Collections.Generic; namespace Luneraria.Core { public enum Phase { Planning, Combat, Over } /// One persistent troop of a player (committed roster or planning draft). public sealed class RosterEntry { /// "p{slot}-{seq}": unique per match; ownership is the slot in the uid. public string Uid = ""; public int Seq; public string TypeId = ""; /// top, mid, bot or grotto (one round). public string Lane = ""; public int Invested; /// Bought in the current planning (full refund on sale). public bool Fresh; public RosterEntry Clone() { return (RosterEntry)MemberwiseClone(); } } /// The single hero of a player. public sealed class HeroState { public string Uid = ""; public int Seq; public string HeroId = ""; public string Lane = ""; public int Xp; public List Items = new List(); public List Talents = new List(); public int Invested; public bool Fresh; /// Items bought in the current planning (full refund on sale). public List FreshItems = new List(); /// Trigger rule of the ultimate (H1), "" = the hero's default rule. public string Ultimate = ""; /// H1: cosmetic skin ("" = standard), chosen freely in planning, visible to everyone. public string Skin = ""; /// S1 (extended rules): fell in the last combat; starts the next one weakened unless bought back. public bool Exhausted; public HeroState Clone() { var copy = (HeroState)MemberwiseClone(); copy.Items = new List(Items); copy.Talents = new List(Talents); copy.FreshItems = new List(FreshItems); return copy; } } /// An attack champion booking: bought in round n, fights once in round n + 1. public sealed class Booking { public string Uid = ""; public int Seq; public string ChampionId = ""; public string Lane = ""; public int Cost; /// 0 while uncommitted (draft); the round it fights in once committed. public int DeployRound; public Booking Clone() { return (Booking)MemberwiseClone(); } } /// Private planning of one player; validated and committed atomically at combat start. public sealed class Draft { public int Gold; public List Roster = new List(); public int NextSeq; public HeroState Hero; public List NewBookings = new List(); /// Lane chosen by the boss chooser in this planning, "" if unchanged. public string BossLane = ""; /// S2: lane the player holds the team glyph ready for, "" = none. public string GlyphLane = ""; /// S2: Ancient levels this player buys in this planning. public int CoreInvest; /// S3: lanes the player places a ward on in this planning. public List WardLanes = new List(); public Draft Clone() { var copy = new Draft { Gold = Gold, NextSeq = NextSeq, BossLane = BossLane, GlyphLane = GlyphLane, CoreInvest = CoreInvest, Hero = Hero != null ? Hero.Clone() : null, WardLanes = new List(WardLanes) }; foreach (RosterEntry entry in Roster) copy.Roster.Add(entry.Clone()); foreach (Booking booking in NewBookings) copy.NewBookings.Add(booking.Clone()); return copy; } } /// One of six seats. Team = slot / 3, lane = slot % 3 (top, mid, bot). public sealed class Player { public int Slot; public int Team; public string Lane = ""; public bool IsHuman; public string Name = ""; public string Bund = ""; public int Gold; public List Roster = new List(); public HeroState Hero; public List Bookings = new List(); public bool Ready; public int Earned; public int Spent; public int NextSeq; public Draft Draft; public List Undo = new List(); /// S1 (extended rules): permanent income from booked attack champions (Legion TD 2). public int IncomeBonus; } public sealed class BossClaim { public int Team; public int WonRound; public int DeployRound; public int ChooserSlot; public string Lane = ""; public BossClaim Clone() { return (BossClaim)MemberwiseClone(); } } /// Public boss state: the grotto opens from NextAvailableRound; Claim is null or the conquest. public sealed class BossState { public int NextAvailableRound = 1; public BossClaim Claim; /// Round whose settlement was already applied (idempotence), 0 = none. public int SettledRound; /// S1 (extended rules): Mondsegen stacks per team from grotto victories (League of Legends dragon souls). public int[] Blessing = new int[2]; /// S2 (extended rules): first round each team's glyph can be held again (Dota 2). public int[] GlyphReady = { 1, 1 }; /// S2 (extended rules): Ancient level per team, 0..3 (Legion TD 2 king upgrades). public int[] CoreLevel = new int[2]; /// S3 (extended rules): combats left of the camp buffs, index team × 3 + lane (top Glutsegen, mid Mondschild). public int[] CampBuff = new int[6]; /// S3 (extended rules): rounds left of each team's ward per lane (team × 3 + lane). public int[] Wards = new int[6]; } public sealed class BossLogEntry { public int Round; /// deployed, won or failed. public string Event = ""; public int Team = -1; public string Lane = ""; public int Chooser = -1; } /// Public announcement of the current round: a champion or the deployed boss. public sealed class Announcement { /// champion or boss. public string Kind = ""; public int Team; public int Slot; public string Lane = ""; public string ChampionId = ""; public int Bounty; } public sealed class Settlement { public int Round; public int[] Gains = new int[6]; public int Income; public List BuildingsLost = new List(); public string EndReason = ""; public double CombatTime; public OrderedMap LaneKills = new OrderedMap(); public OrderedMap Xp = new OrderedMap(); /// none, won, failed, deployed or repeat. public string Boss = "none"; } public sealed class HistoryEntry { public int Round; public string EndReason = ""; public double CombatTime; public int[] Buildings = new int[2]; public int[] Gold = new int[6]; public List Lost = new List(); public string Boss = ""; public int[] HeroLevels = new int[6]; } public sealed class MatchOptions { public int Seed = 1; /// -1 for six bots. public int HumanSlot = 1; /// Six Bünde; missing entries follow the seed. public List Bunds = new List(); /// Optional fixed bot profile per slot (balance runs). public List Profiles = new List(); /// /// S1: extended rules after Legion TD 2, Dota 2 and League of Legends (champion income, /// barracks, exhaustion and buyback, Mondsegen) and bots that may pick the heroes after /// the reference. False = the reference rules of the port (golden replays, fingerprints). /// public bool Extended; } /// Bot-private notes per slot, derived only from its own actions and public outcomes. public sealed class BotMemory { public bool HasChampionRound; public int ChampionRound; public bool HasBossFields; public double Bar; public int Sent; } public struct CommandResult { public bool Ok; public string Message; public CommandResult(bool ok, string message) { Ok = ok; Message = message; } } /// /// A player's intent during planning. Clients only ever send these; the authority applies /// them through LuneMatch.Submit. Null fields mean "not given" (e.g. buy without lane uses /// the player's own lane). /// public sealed class Command { public string Type = ""; public string TypeId; public string Lane; public string Uid; public string HeroId; public string ItemId; public string ChampionId; public int? Tier; public int? Choice; public static Command Buy(string typeId, string lane = null) { return new Command { Type = "buy", TypeId = typeId, Lane = lane }; } public static Command Upgrade(string uid) { return new Command { Type = "upgrade", Uid = uid }; } public static Command Sell(string uid) { return new Command { Type = "sell", Uid = uid }; } public static Command Assign(string uid, string lane) { return new Command { Type = "assign", Uid = uid, Lane = lane }; } public static Command HeroBuy(string heroId) { return new Command { Type = "hero_buy", HeroId = heroId }; } public static Command HeroCancel() { return new Command { Type = "hero_cancel" }; } public static Command ItemBuy(string itemId, string uid = null) { return new Command { Type = "item_buy", ItemId = itemId, Uid = uid }; } public static Command ItemSell(string itemId) { return new Command { Type = "item_sell", ItemId = itemId }; } public static Command Talent(int tier, int choice) { return new Command { Type = "talent", Tier = tier, Choice = choice }; } public static Command ChampionBook(string championId, string lane = null) { return new Command { Type = "champion_book", ChampionId = championId, Lane = lane }; } public static Command ChampionCancel(string uid) { return new Command { Type = "champion_cancel", Uid = uid }; } public static Command BossLaneChoice(string lane) { return new Command { Type = "boss_lane", Lane = lane }; } /// H1: trigger rule of the hero's ultimate; the rule id travels in TypeId. public static Command UltimateRule(string rule) { return new Command { Type = "ultimate_rule", TypeId = rule }; } /// H1: cosmetic skin of the hero ("" = standard); the skin id travels in TypeId. public static Command HeroSkin(string skin) { return new Command { Type = "hero_skin", TypeId = skin }; } /// S1: buys an exhausted hero back to full strength for the next combat. public static Command Buyback() { return new Command { Type = "buyback" }; } /// S2: hold the team glyph for a lane in the coming combat ("" withdraws it). public static Command Glyph(string lane) { return new Command { Type = "glyph", Lane = lane }; } /// S2: buy the next Ancient level for the team. public static Command CoreUpgrade() { return new Command { Type = "core_upgrade" }; } /// S3: place a ward on a lane (fog of war). public static Command Ward(string lane) { return new Command { Type = "ward", Lane = lane }; } } }