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 = "";
public Draft Clone()
{
var copy = new Draft { Gold = Gold, NextSeq = NextSeq, BossLane = BossLane, Hero = Hero != null ? Hero.Clone() : null };
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];
}
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" }; }
}
}