Vollständiger, engineunabhängiger Regelkern (Luneraria.Core, ohne UnityEngine): Partie mit sechs Plätzen und zwei Teams, private Planung mit atomarer Übernahme, Gold, Käufe, Aufwertungen, Formation, drei Lanes mit zwei Laneturmen und zwei Basistürmen je Team, Ancients, unbegrenzte Wellen, Helden mit Stufen, Items und Talenten, Angriffschampions, Bossgrotte mit Anspruch, Entsendung, Kopfgeld und dreirundiger Sperre, Bots und Siegbedingung (nur Ancient-Fall). Serverautoritative Befehlsschnittstelle. - Deterministisch: fester Takt, geordnete Maps, fortlaufende IDs, kein Zufall, keine Uhr, keine Threads - Tabellen einmal aus Godot erzeugt (tools/port/gen_content.py) - Golden-Referenzen aus Godot (tools/godot-golden): 19 Szenarien, 120 Snapshots, 51.221 Werte, Abweichung 0 (Toleranzen dokumentiert) - Tests: content 75, core 91, m2 73, golden 157, determinism 6 = 402/402 - Leistung (M5): Simulationsschritt 0,259 ms statt 1,587 ms in GDScript - Referenzfehler R1 im Bot-Gedächtnis bewusst übernommen und dokumentiert - Geplante Helden (Lucy, Ghaliga, Siegfried, Feena) im Datenmodell, inaktiv - Doku unter docs/port (U1-RULES, PORT-STATUS, PORT-QA, U2 bis U5) Unity-Editor-Lauf offen: Die Unity-Lizenz ist noch nicht aktiviert; gebaut und getestet mit dem .NET 8 SDK auf Unitys Sprachstand (C# 9, .NET Standard 2.1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
200 lines
6.3 KiB
C#
200 lines
6.3 KiB
C#
using System.Collections.Generic;
|
|
|
|
namespace Luneraria.Core
|
|
{
|
|
/// <summary>
|
|
/// Combat definition of a unit type: troop, monster, send, hero, champion, boss or building.
|
|
/// One type for all, as in the reference, where definitions were dictionaries; the defaults
|
|
/// are the fallbacks the rules used for missing keys (e.g. SiegeFactor 1, HpFactor 1).
|
|
/// </summary>
|
|
public sealed class UnitDef
|
|
{
|
|
public string Id = "";
|
|
public string DisplayName = "";
|
|
public int Cost;
|
|
public string UpgradeId = "";
|
|
public double MaxHp = 1.0;
|
|
public double Damage;
|
|
/// <summary>0 Klinge, 1 Durchschlag, 2 Impuls.</summary>
|
|
public int AttackType;
|
|
/// <summary>0 Leicht, 1 Schwer, 2 Ätherisch.</summary>
|
|
public int ArmorType;
|
|
public double AttackRange = 1.0;
|
|
public double Speed = 2.0;
|
|
public string Ability = "";
|
|
public int LeakValue;
|
|
public int Bounty;
|
|
public string AbilityDescription = "";
|
|
public bool IsBoss;
|
|
public int IncomeGain;
|
|
// Set by the match for army units and heroes.
|
|
public string Role = "";
|
|
public string HeroId = "";
|
|
public int Level = 1;
|
|
public double ViewScale = 1.0;
|
|
public double Regen;
|
|
public double ItemSplash;
|
|
public List<string> Talents = new List<string>();
|
|
public double DamageBonus;
|
|
public double ReductionBonus;
|
|
public double AttackSpeedBonus;
|
|
// Attack champions.
|
|
public string ChampionId = "";
|
|
public string Priority = "";
|
|
public double SiegeFactor = 1.0;
|
|
// Monsters: wave strength factor (heals of e06 scale with it).
|
|
public double HpFactor = 1.0;
|
|
// Buildings: outer, inner, base or core.
|
|
public string Tier = "";
|
|
|
|
public UnitDef Clone()
|
|
{
|
|
var copy = (UnitDef)MemberwiseClone();
|
|
copy.Talents = new List<string>(Talents);
|
|
return copy;
|
|
}
|
|
}
|
|
|
|
public sealed class HeroSpec
|
|
{
|
|
public string Id = "";
|
|
public string Name = "";
|
|
public string Title = "";
|
|
public string Model = "";
|
|
public int Cost;
|
|
public string RoleText = "";
|
|
public bool Provisional;
|
|
public string Summary = "";
|
|
/// <summary>Level-1 values without items and talents.</summary>
|
|
public UnitDef Base = new UnitDef();
|
|
}
|
|
|
|
public sealed class TalentOption
|
|
{
|
|
public readonly string Id;
|
|
public readonly string Name;
|
|
public readonly string Text;
|
|
|
|
public TalentOption(string id, string name, string text)
|
|
{
|
|
Id = id;
|
|
Name = name;
|
|
Text = text;
|
|
}
|
|
}
|
|
|
|
public sealed class ItemSpec
|
|
{
|
|
public string Id = "";
|
|
public string Name = "";
|
|
public int Cost;
|
|
public string Text = "";
|
|
public double HpFlat;
|
|
public double Damage;
|
|
public double Reduction;
|
|
public double AttackSpeed;
|
|
public double Regen;
|
|
public double Splash;
|
|
public int MinLevel = 1;
|
|
}
|
|
|
|
public sealed class ChampionSpec
|
|
{
|
|
public string Id = "";
|
|
public string Name = "";
|
|
public string RoleText = "";
|
|
public string Model = "";
|
|
public int Cost;
|
|
/// <summary>Send type whose base values the champion scales (s02, s03, s04).</summary>
|
|
public string Base = "";
|
|
public string Text = "";
|
|
public string Priority = "";
|
|
public double SiegeFactor = 1.0;
|
|
public double ViewScale = 1.0;
|
|
}
|
|
|
|
public sealed class BotProfile
|
|
{
|
|
public string Name = "";
|
|
public double Front;
|
|
public double Melee;
|
|
public double Ranged;
|
|
public double Splash;
|
|
public double Support;
|
|
public int UpgradeFrom;
|
|
public double SupportLane;
|
|
public string Hero = "";
|
|
public int HeroRound;
|
|
public string[] Items = new string[0];
|
|
public int[] TalentChoices = new int[0];
|
|
public string Champion = "";
|
|
/// <summary>Boss hunters only; 0 = never goes to the grotto.</summary>
|
|
public int BossFrom;
|
|
public int BossTroops;
|
|
|
|
public bool HuntsBoss { get { return BossFrom > 0; } }
|
|
|
|
public double Share(string role)
|
|
{
|
|
switch (role)
|
|
{
|
|
case "front": return Front;
|
|
case "melee": return Melee;
|
|
case "ranged": return Ranged;
|
|
case "splash": return Splash;
|
|
default: return Support;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static partial class ContentData
|
|
{
|
|
static OrderedMap<string, UnitDef> Map(params UnitDef[] entries)
|
|
{
|
|
var map = new OrderedMap<string, UnitDef>();
|
|
foreach (UnitDef entry in entries)
|
|
map[entry.Id] = entry;
|
|
return map;
|
|
}
|
|
|
|
static OrderedMap<string, HeroSpec> Ordered(params HeroSpec[] entries)
|
|
{
|
|
var map = new OrderedMap<string, HeroSpec>();
|
|
foreach (HeroSpec entry in entries)
|
|
map[entry.Id] = entry;
|
|
return map;
|
|
}
|
|
|
|
static OrderedMap<string, ItemSpec> Ordered(params ItemSpec[] entries)
|
|
{
|
|
var map = new OrderedMap<string, ItemSpec>();
|
|
foreach (ItemSpec entry in entries)
|
|
map[entry.Id] = entry;
|
|
return map;
|
|
}
|
|
|
|
static OrderedMap<string, ChampionSpec> Ordered(params ChampionSpec[] entries)
|
|
{
|
|
var map = new OrderedMap<string, ChampionSpec>();
|
|
foreach (ChampionSpec entry in entries)
|
|
map[entry.Id] = entry;
|
|
return map;
|
|
}
|
|
|
|
static OrderedMap<string, BotProfile> Ordered(params BotProfile[] entries)
|
|
{
|
|
var map = new OrderedMap<string, BotProfile>();
|
|
foreach (BotProfile entry in entries)
|
|
map[entry.Name] = entry;
|
|
return map;
|
|
}
|
|
|
|
static OrderedMap<string, string> Pairs(params string[] keysAndValues)
|
|
{
|
|
var map = new OrderedMap<string, string>();
|
|
for (int i = 0; i + 1 < keysAndValues.Length; i += 2)
|
|
map[keysAndValues[i]] = keysAndValues[i + 1];
|
|
return map;
|
|
}
|
|
}
|
|
}
|