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>
|
|
@ -60,8 +60,26 @@ namespace Luneraria.Core
|
||||||
public double OuterTowerFraction = 0.35;
|
public double OuterTowerFraction = 0.35;
|
||||||
public double TowerSideOffset = 3.4;
|
public double TowerSideOffset = 3.4;
|
||||||
|
|
||||||
|
// S1: extended rules (MatchOptions.Extended; the reference rules ignore all of these).
|
||||||
|
/// <summary>Legion TD 2: a booked attack champion raises the booker's income by cost / this, for good.</summary>
|
||||||
|
public int ChampionIncomeDivisor = 12;
|
||||||
|
/// <summary>Dota 2 barracks: waves of a team on a lane whose enemy inner tower fell get stronger.</summary>
|
||||||
|
public double BarracksBoost = 0.25;
|
||||||
|
public int BarracksExtraMonsters = 1;
|
||||||
|
/// <summary>Dota 2 buyback, adapted: a fallen hero starts the next combat exhausted, unless bought back.</summary>
|
||||||
|
public double ExhaustedHp = 0.6;
|
||||||
|
public int BuybackBase = 60;
|
||||||
|
public int BuybackPerLevel = 20;
|
||||||
|
/// <summary>League of Legends dragon souls: each grotto victory blesses the team, up to the soul.</summary>
|
||||||
|
public double BlessingStep = 0.04;
|
||||||
|
public int BlessingMax = 4;
|
||||||
|
/// <summary>Moon soul (all stacks): heroes regenerate this share of their health per second.</summary>
|
||||||
|
public double SoulRegen = 0.01;
|
||||||
|
|
||||||
public static LuneBalance Default() { return new LuneBalance(); }
|
public static LuneBalance Default() { return new LuneBalance(); }
|
||||||
|
|
||||||
|
public int BuybackCost(int level) { return BuybackBase + BuybackPerLevel * level; }
|
||||||
|
|
||||||
public int RoundIncome(int roundNumber)
|
public int RoundIncome(int roundNumber)
|
||||||
{
|
{
|
||||||
return BaseIncome + GdMath.Min(IncomeGrowthCap, IncomePerRound * GdMath.Max(0, roundNumber - 1));
|
return BaseIncome + GdMath.Min(IncomeGrowthCap, IncomePerRound * GdMath.Max(0, roundNumber - 1));
|
||||||
|
|
|
||||||
|
|
@ -571,6 +571,7 @@ namespace Luneraria.Core
|
||||||
player.Gold = row.Gold;
|
player.Gold = row.Gold;
|
||||||
player.Ready = row.Ready;
|
player.Ready = row.Ready;
|
||||||
player.Earned = row.Earned;
|
player.Earned = row.Earned;
|
||||||
|
player.IncomeBonus = row.IncomeBonus;
|
||||||
player.Spent = row.Spent;
|
player.Spent = row.Spent;
|
||||||
player.NextSeq = row.NextSeq;
|
player.NextSeq = row.NextSeq;
|
||||||
player.Roster = new List<RosterEntry>();
|
player.Roster = new List<RosterEntry>();
|
||||||
|
|
@ -621,6 +622,8 @@ namespace Luneraria.Core
|
||||||
MatchState.Boss.NextAvailableRound = source.NextAvailableRound;
|
MatchState.Boss.NextAvailableRound = source.NextAvailableRound;
|
||||||
MatchState.Boss.SettledRound = source.SettledRound;
|
MatchState.Boss.SettledRound = source.SettledRound;
|
||||||
MatchState.Boss.Claim = source.Claim != null ? source.Claim.Clone() : null;
|
MatchState.Boss.Claim = source.Claim != null ? source.Claim.Clone() : null;
|
||||||
|
if (source.Blessing != null && source.Blessing.Length == 2)
|
||||||
|
MatchState.Boss.Blessing = (int[])source.Blessing.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyLobby(List<LobbyEntry> entries)
|
public void ApplyLobby(List<LobbyEntry> entries)
|
||||||
|
|
@ -857,7 +860,8 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
NextAvailableRound = MatchState.Boss.NextAvailableRound,
|
NextAvailableRound = MatchState.Boss.NextAvailableRound,
|
||||||
SettledRound = MatchState.Boss.SettledRound,
|
SettledRound = MatchState.Boss.SettledRound,
|
||||||
Claim = MatchState.Boss.Claim != null ? MatchState.Boss.Claim.Clone() : null
|
Claim = MatchState.Boss.Claim != null ? MatchState.Boss.Claim.Clone() : null,
|
||||||
|
Blessing = (int[])MatchState.Boss.Blessing.Clone()
|
||||||
};
|
};
|
||||||
if (MatchState.Combat != null)
|
if (MatchState.Combat != null)
|
||||||
state.DestroyedCores.AddRange(MatchState.Combat.DestroyedCores);
|
state.DestroyedCores.AddRange(MatchState.Combat.DestroyedCores);
|
||||||
|
|
@ -872,6 +876,7 @@ namespace Luneraria.Core
|
||||||
Gold = player.Gold,
|
Gold = player.Gold,
|
||||||
Ready = player.Ready,
|
Ready = player.Ready,
|
||||||
Earned = player.Earned,
|
Earned = player.Earned,
|
||||||
|
IncomeBonus = player.IncomeBonus,
|
||||||
Spent = player.Spent,
|
Spent = player.Spent,
|
||||||
NextSeq = player.NextSeq,
|
NextSeq = player.NextSeq,
|
||||||
Hero = player.Hero != null ? player.Hero.Clone() : null,
|
Hero = player.Hero != null ? player.Hero.Clone() : null,
|
||||||
|
|
@ -930,7 +935,7 @@ namespace Luneraria.Core
|
||||||
|
|
||||||
MatchOptions PublicOptions()
|
MatchOptions PublicOptions()
|
||||||
{
|
{
|
||||||
var options = new MatchOptions { Seed = MatchState != null ? MatchState.SeedValue : 1, HumanSlot = LocalSlot };
|
var options = new MatchOptions { Seed = MatchState != null ? MatchState.SeedValue : 1, HumanSlot = LocalSlot, Extended = MatchState != null && MatchState.Extended };
|
||||||
if (MatchState != null)
|
if (MatchState != null)
|
||||||
{
|
{
|
||||||
foreach (Player player in MatchState.Players)
|
foreach (Player player in MatchState.Players)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ namespace Luneraria.Core
|
||||||
public Draft Draft;
|
public Draft Draft;
|
||||||
/// <summary>Undo stack size of the receiving seat (0 for others).</summary>
|
/// <summary>Undo stack size of the receiving seat (0 for others).</summary>
|
||||||
public int Undo;
|
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>
|
/// <summary>Public state of the match for one seat (rpc_state).</summary>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@ namespace Luneraria.Core
|
||||||
// A match may fix the profile per slot (balance runs); otherwise a seed-based rotation.
|
// A match may fix the profile per slot (balance runs); otherwise a seed-based rotation.
|
||||||
if (match.BotProfiles.Count == 6)
|
if (match.BotProfiles.Count == 6)
|
||||||
return match.BotProfiles[slot];
|
return match.BotProfiles[slot];
|
||||||
IReadOnlyList<string> names = ContentData.BotProfiles.Keys;
|
// S1: with extended rules the rotation also holds the profiles of later heroes (Sylvara).
|
||||||
|
var names = new List<string>(ContentData.BotProfiles.Keys);
|
||||||
|
if (match.Extended)
|
||||||
|
names.AddRange(ContentData.ExtraBotProfiles.Keys);
|
||||||
return names[(slot * 7 + match.SeedValue) % names.Count];
|
return names[(slot * 7 + match.SeedValue) % names.Count];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +79,9 @@ namespace Luneraria.Core
|
||||||
match.Submit(slot, Command.HeroBuy(profile.Hero));
|
match.Submit(slot, Command.HeroBuy(profile.Hero));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// S1: buy an exhausted hero back while the army keeps a reserve.
|
||||||
|
if (hero.Exhausted && match.Extended && player.Draft.Gold >= match.BuybackCost(hero) + 150)
|
||||||
|
match.Submit(slot, Command.Buyback());
|
||||||
int level = LuneContent.LevelFor(hero.Xp);
|
int level = LuneContent.LevelFor(hero.Xp);
|
||||||
for (int tier = 0; tier < LuneContent.TalentLevels.Length; tier++)
|
for (int tier = 0; tier < LuneContent.TalentLevels.Length; tier++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ namespace Luneraria.Core
|
||||||
public int RoundNumber = 1;
|
public int RoundNumber = 1;
|
||||||
public Phase Phase = Phase.Planning;
|
public Phase Phase = Phase.Planning;
|
||||||
public int Winner = Ongoing;
|
public int Winner = Ongoing;
|
||||||
|
/// <summary>S1: extended rules (MatchOptions.Extended); false keeps the reference rules exactly.</summary>
|
||||||
|
public bool Extended;
|
||||||
public string EndNote = "";
|
public string EndNote = "";
|
||||||
public readonly List<Player> Players = new List<Player>();
|
public readonly List<Player> Players = new List<Player>();
|
||||||
/// <summary>Building HP between combats, in map order.</summary>
|
/// <summary>Building HP between combats, in map order.</summary>
|
||||||
|
|
@ -46,6 +48,8 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
options = options ?? new MatchOptions();
|
options = options ?? new MatchOptions();
|
||||||
SeedValue = options.Seed;
|
SeedValue = options.Seed;
|
||||||
|
Extended = options.Extended;
|
||||||
|
Boss.Blessing = new int[2];
|
||||||
RoundNumber = 1;
|
RoundNumber = 1;
|
||||||
Phase = Phase.Planning;
|
Phase = Phase.Planning;
|
||||||
Winner = Ongoing;
|
Winner = Ongoing;
|
||||||
|
|
@ -316,6 +320,19 @@ namespace Luneraria.Core
|
||||||
hero.Skin = skin;
|
hero.Skin = skin;
|
||||||
return Result(true, skin.Length == 0 ? "Standard-Aussehen gewählt." : "Skin gewählt.");
|
return Result(true, skin.Length == 0 ? "Standard-Aussehen gewählt." : "Skin gewählt.");
|
||||||
}
|
}
|
||||||
|
case "buyback":
|
||||||
|
{
|
||||||
|
if (!Extended)
|
||||||
|
return Result(false, "Rückkauf gibt es nur mit erweiterten Regeln.");
|
||||||
|
if (hero == null || !hero.Exhausted)
|
||||||
|
return Result(false, "Dein Held ist nicht erschöpft.");
|
||||||
|
int cost = BuybackCost(hero);
|
||||||
|
if (draft.Gold < cost)
|
||||||
|
return Result(false, string.Format("Es fehlen {0} Gold.", cost - draft.Gold));
|
||||||
|
draft.Gold -= cost;
|
||||||
|
hero.Exhausted = false;
|
||||||
|
return Result(true, string.Format("Held für {0} Gold zurückgekauft.", cost));
|
||||||
|
}
|
||||||
case "champion_book":
|
case "champion_book":
|
||||||
{
|
{
|
||||||
string championId = command.ChampionId ?? "";
|
string championId = command.ChampionId ?? "";
|
||||||
|
|
@ -433,6 +450,9 @@ namespace Luneraria.Core
|
||||||
Booking committed = booking.Clone();
|
Booking committed = booking.Clone();
|
||||||
committed.DeployRound = RoundNumber + 1;
|
committed.DeployRound = RoundNumber + 1;
|
||||||
player.Bookings.Add(committed);
|
player.Bookings.Add(committed);
|
||||||
|
// S1 (Legion TD 2): sending raises the own income for good.
|
||||||
|
if (Extended)
|
||||||
|
player.IncomeBonus += ChampionIncome(booking.Cost);
|
||||||
}
|
}
|
||||||
BossClaim claim = Boss.Claim;
|
BossClaim claim = Boss.Claim;
|
||||||
if (claim != null && claim.ChooserSlot == player.Slot && draft.BossLane.Length > 0)
|
if (claim != null && claim.ChooserSlot == player.Slot && draft.BossLane.Length > 0)
|
||||||
|
|
@ -499,16 +519,74 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
foreach (string lane in LuneMap.Lanes)
|
foreach (string lane in LuneMap.Lanes)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < wave.Count; index++)
|
List<UnitDef> lineup = Extended && BarracksBroken(team, lane) ? Reinforced(wave) : wave;
|
||||||
|
for (int index = 0; index < lineup.Count; index++)
|
||||||
{
|
{
|
||||||
int row = index / 3;
|
int row = index / 3;
|
||||||
double lateral = ((double)(index % 3) - 1.0) * 1.3;
|
double lateral = ((double)(index % 3) - 1.0) * 1.3;
|
||||||
Combat.Spawn("monster", team, lane, wave[index].Clone(), 11.0 - (double)row * 1.4, lateral);
|
Combat.Spawn("monster", team, lane, lineup[index].Clone(), 11.0 - (double)row * 1.4, lateral);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- S1 extended rules
|
||||||
|
/// <summary>Legion TD 2: income a booked champion adds for good.</summary>
|
||||||
|
public int ChampionIncome(int cost) { return cost / GdMath.Max(1, Balance.ChampionIncomeDivisor); }
|
||||||
|
|
||||||
|
public int BuybackCost(HeroState hero) { return Balance.BuybackCost(LuneContent.LevelFor(hero.Xp)); }
|
||||||
|
|
||||||
|
/// <summary>Dota 2 barracks: the enemy inner tower of this lane has fallen, so this team's waves there are reinforced.</summary>
|
||||||
|
public bool BarracksBroken(int team, string lane)
|
||||||
|
{
|
||||||
|
foreach (BuildingDef definition in Map.Buildings)
|
||||||
|
{
|
||||||
|
if (definition.Team == 1 - team && definition.Lane == lane && definition.Tier == "inner")
|
||||||
|
return BuildingHp[definition.Id] <= 0.0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>The wave stronger by BarracksBoost and longer by extra copies of its first monster.</summary>
|
||||||
|
List<UnitDef> Reinforced(List<UnitDef> wave)
|
||||||
|
{
|
||||||
|
var result = new List<UnitDef>();
|
||||||
|
foreach (UnitDef monster in wave)
|
||||||
|
{
|
||||||
|
UnitDef strong = monster.Clone();
|
||||||
|
strong.MaxHp *= 1.0 + Balance.BarracksBoost;
|
||||||
|
strong.Damage *= 1.0 + Balance.BarracksBoost;
|
||||||
|
result.Add(strong);
|
||||||
|
}
|
||||||
|
for (int extra = 0; extra < Balance.BarracksExtraMonsters && result.Count > 0; extra++)
|
||||||
|
result.Insert(0, result[0].Clone());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>League of Legends dragon souls: Mondsegen stacks raise health and damage; the soul adds hero regeneration.</summary>
|
||||||
|
UnitDef Blessed(UnitDef definition, int team, string kind)
|
||||||
|
{
|
||||||
|
int stacks = GdMath.Min(Boss.Blessing[team], Balance.BlessingMax);
|
||||||
|
if (stacks <= 0)
|
||||||
|
return definition;
|
||||||
|
double factor = 1.0 + Balance.BlessingStep * stacks;
|
||||||
|
definition.MaxHp *= factor;
|
||||||
|
definition.Damage *= factor;
|
||||||
|
if (stacks >= Balance.BlessingMax && kind == "hero")
|
||||||
|
definition.Regen += Balance.SoulRegen;
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>S1: an exhausted hero starts its combat with ExhaustedHp of its health.</summary>
|
||||||
|
void Exhaust(LuneUnit unit)
|
||||||
|
{
|
||||||
|
if (!Extended || unit.Kind != "hero" || unit.OwnerSlot < 0)
|
||||||
|
return;
|
||||||
|
HeroState hero = Players[unit.OwnerSlot].Hero;
|
||||||
|
if (hero != null && hero.Exhausted && hero.Uid == unit.PersistentUid)
|
||||||
|
unit.Hp = unit.MaxHp * Balance.ExhaustedHp;
|
||||||
|
}
|
||||||
|
|
||||||
void SpawnChampions()
|
void SpawnChampions()
|
||||||
{
|
{
|
||||||
for (int team = 0; team < 2; team++)
|
for (int team = 0; team < 2; team++)
|
||||||
|
|
@ -624,7 +702,9 @@ namespace Luneraria.Core
|
||||||
ArmyEntry entry = ordered[index];
|
ArmyEntry entry = ordered[index];
|
||||||
int row = index / 4;
|
int row = index / 4;
|
||||||
double lateral = ((double)(index % 4) - 1.5) * 1.2;
|
double lateral = ((double)(index % 4) - 1.5) * 1.2;
|
||||||
Combat.Spawn(entry.Kind, team, lane, entry.Definition, GdMath.Max(0.4, 7.2 - (double)row * 1.5), lateral, entry.Slot, entry.Uid);
|
LuneUnit spawned = Combat.Spawn(entry.Kind, team, lane, Extended ? Blessed(entry.Definition, team, entry.Kind) : entry.Definition,
|
||||||
|
GdMath.Max(0.4, 7.2 - (double)row * 1.5), lateral, entry.Slot, entry.Uid);
|
||||||
|
Exhaust(spawned);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -642,7 +722,8 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
ArmyEntry entry = ordered[index];
|
ArmyEntry entry = ordered[index];
|
||||||
Vec2 at = entryPoint - inward * (double)(index / 3) * 1.2 + across * ((double)(index % 3) - 1.0) * 1.1;
|
Vec2 at = entryPoint - inward * (double)(index / 3) * 1.2 + across * ((double)(index % 3) - 1.0) * 1.1;
|
||||||
Combat.SpawnAt(entry.Kind, team, Grotto, entry.Definition, at, entry.Slot, entry.Uid);
|
LuneUnit spawned = Combat.SpawnAt(entry.Kind, team, Grotto, Extended ? Blessed(entry.Definition, team, entry.Kind) : entry.Definition, at, entry.Slot, entry.Uid);
|
||||||
|
Exhaust(spawned);
|
||||||
expedition = true;
|
expedition = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -727,10 +808,19 @@ namespace Luneraria.Core
|
||||||
if (player.Hero != null && xpGain.TryGetValue(player.Hero.Uid, out gained))
|
if (player.Hero != null && xpGain.TryGetValue(player.Hero.Uid, out gained))
|
||||||
player.Hero.Xp += gained;
|
player.Hero.Xp += gained;
|
||||||
}
|
}
|
||||||
|
if (Extended)
|
||||||
|
{
|
||||||
|
// S1 (Dota 2 buyback, adapted): a hero that fell is exhausted for the next combat.
|
||||||
|
foreach (LuneUnit unit in Combat.Units)
|
||||||
|
{
|
||||||
|
if (unit.Kind == "hero" && unit.OwnerSlot >= 0 && Players[unit.OwnerSlot].Hero != null && Players[unit.OwnerSlot].Hero.Uid == unit.PersistentUid)
|
||||||
|
Players[unit.OwnerSlot].Hero.Exhausted = !unit.Alive;
|
||||||
|
}
|
||||||
|
}
|
||||||
int income = Balance.RoundIncome(RoundNumber);
|
int income = Balance.RoundIncome(RoundNumber);
|
||||||
foreach (Player player in Players)
|
foreach (Player player in Players)
|
||||||
{
|
{
|
||||||
int total = gains[player.Slot] + (Combat.DestroyedCores.Count > 0 ? 0 : income);
|
int total = gains[player.Slot] + (Combat.DestroyedCores.Count > 0 ? 0 : income + (Extended ? player.IncomeBonus : 0));
|
||||||
player.Gold += total;
|
player.Gold += total;
|
||||||
player.Earned += total;
|
player.Earned += total;
|
||||||
// Deployed bookings are consumed; they never return as property.
|
// Deployed bookings are consumed; they never return as property.
|
||||||
|
|
@ -801,6 +891,9 @@ namespace Luneraria.Core
|
||||||
Boss.Claim = new BossClaim { Team = team, WonRound = fought, DeployRound = fought + 1, ChooserSlot = chooser, Lane = Players[chooser].Lane };
|
Boss.Claim = new BossClaim { Team = team, WonRound = fought, DeployRound = fought + 1, ChooserSlot = chooser, Lane = Players[chooser].Lane };
|
||||||
Boss.NextAvailableRound = fought + 1 + LuneContent.BossLockRounds;
|
Boss.NextAvailableRound = fought + 1 + LuneContent.BossLockRounds;
|
||||||
BossLog.Add(new BossLogEntry { Round = fought, Event = "won", Team = team, Chooser = chooser });
|
BossLog.Add(new BossLogEntry { Round = fought, Event = "won", Team = team, Chooser = chooser });
|
||||||
|
// S1 (League of Legends dragon souls): each conquest blesses the team for good.
|
||||||
|
if (Extended)
|
||||||
|
Boss.Blessing[team] = GdMath.Min(Balance.BlessingMax, Boss.Blessing[team] + 1);
|
||||||
result = "won";
|
result = "won";
|
||||||
}
|
}
|
||||||
else if (Combat.GrottoOutcome == "failed")
|
else if (Combat.GrottoOutcome == "failed")
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ namespace Luneraria.Core
|
||||||
public string Ultimate = "";
|
public string Ultimate = "";
|
||||||
/// <summary>H1: cosmetic skin ("" = standard), chosen freely in planning, visible to everyone.</summary>
|
/// <summary>H1: cosmetic skin ("" = standard), chosen freely in planning, visible to everyone.</summary>
|
||||||
public string Skin = "";
|
public string Skin = "";
|
||||||
|
/// <summary>S1 (extended rules): fell in the last combat; starts the next one weakened unless bought back.</summary>
|
||||||
|
public bool Exhausted;
|
||||||
|
|
||||||
public HeroState Clone()
|
public HeroState Clone()
|
||||||
{
|
{
|
||||||
|
|
@ -104,6 +106,8 @@ namespace Luneraria.Core
|
||||||
public int NextSeq;
|
public int NextSeq;
|
||||||
public Draft Draft;
|
public Draft Draft;
|
||||||
public List<Draft> Undo = new List<Draft>();
|
public List<Draft> Undo = new List<Draft>();
|
||||||
|
/// <summary>S1 (extended rules): permanent income from booked attack champions (Legion TD 2).</summary>
|
||||||
|
public int IncomeBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class BossClaim
|
public sealed class BossClaim
|
||||||
|
|
@ -124,6 +128,8 @@ namespace Luneraria.Core
|
||||||
public BossClaim Claim;
|
public BossClaim Claim;
|
||||||
/// <summary>Round whose settlement was already applied (idempotence), 0 = none.</summary>
|
/// <summary>Round whose settlement was already applied (idempotence), 0 = none.</summary>
|
||||||
public int SettledRound;
|
public int SettledRound;
|
||||||
|
/// <summary>S1 (extended rules): Mondsegen stacks per team from grotto victories (League of Legends dragon souls).</summary>
|
||||||
|
public int[] Blessing = new int[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class BossLogEntry
|
public sealed class BossLogEntry
|
||||||
|
|
@ -183,6 +189,12 @@ namespace Luneraria.Core
|
||||||
public List<string> Bunds = new List<string>();
|
public List<string> Bunds = new List<string>();
|
||||||
/// <summary>Optional fixed bot profile per slot (balance runs).</summary>
|
/// <summary>Optional fixed bot profile per slot (balance runs).</summary>
|
||||||
public List<string> Profiles = new List<string>();
|
public List<string> Profiles = new List<string>();
|
||||||
|
/// <summary>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
|
public bool Extended;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Bot-private notes per slot, derived only from its own actions and public outcomes.</summary>
|
/// <summary>Bot-private notes per slot, derived only from its own actions and public outcomes.</summary>
|
||||||
|
|
@ -240,5 +252,7 @@ namespace Luneraria.Core
|
||||||
public static Command UltimateRule(string rule) { return new Command { Type = "ultimate_rule", TypeId = rule }; }
|
public static Command UltimateRule(string rule) { return new Command { Type = "ultimate_rule", TypeId = rule }; }
|
||||||
/// <summary>H1: cosmetic skin of the hero ("" = standard); the skin id travels in TypeId.</summary>
|
/// <summary>H1: cosmetic skin of the hero ("" = standard); the skin id travels in TypeId.</summary>
|
||||||
public static Command HeroSkin(string skin) { return new Command { Type = "hero_skin", TypeId = skin }; }
|
public static Command HeroSkin(string skin) { return new Command { Type = "hero_skin", TypeId = skin }; }
|
||||||
|
/// <summary>S1: buys an exhausted hero back to full strength for the next combat.</summary>
|
||||||
|
public static Command Buyback() { return new Command { Type = "buyback" }; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,7 @@ namespace Luneraria.Net
|
||||||
Int(text, options.HumanSlot);
|
Int(text, options.HumanSlot);
|
||||||
Str(text, options.Bunds != null ? string.Join("\x1d", options.Bunds) : "");
|
Str(text, options.Bunds != null ? string.Join("\x1d", options.Bunds) : "");
|
||||||
Str(text, options.Profiles != null ? string.Join("\x1d", options.Profiles) : "");
|
Str(text, options.Profiles != null ? string.Join("\x1d", options.Profiles) : "");
|
||||||
|
Int(text, options.Extended ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteLobby(StringBuilder text, LobbyEntry entry)
|
static void WriteLobby(StringBuilder text, LobbyEntry entry)
|
||||||
|
|
@ -223,6 +224,8 @@ namespace Luneraria.Net
|
||||||
Int(text, boss.Claim.ChooserSlot);
|
Int(text, boss.Claim.ChooserSlot);
|
||||||
Str(text, boss.Claim.Lane ?? "");
|
Str(text, boss.Claim.Lane ?? "");
|
||||||
}
|
}
|
||||||
|
Int(text, boss.Blessing != null && boss.Blessing.Length > 0 ? boss.Blessing[0] : 0);
|
||||||
|
Int(text, boss.Blessing != null && boss.Blessing.Length > 1 ? boss.Blessing[1] : 0);
|
||||||
var cores = state.DestroyedCores ?? new List<int>();
|
var cores = state.DestroyedCores ?? new List<int>();
|
||||||
Int(text, cores.Count);
|
Int(text, cores.Count);
|
||||||
foreach (int core in cores)
|
foreach (int core in cores)
|
||||||
|
|
@ -262,6 +265,7 @@ namespace Luneraria.Net
|
||||||
Int(text, row.Spent);
|
Int(text, row.Spent);
|
||||||
Int(text, row.NextSeq);
|
Int(text, row.NextSeq);
|
||||||
Int(text, row.Undo);
|
Int(text, row.Undo);
|
||||||
|
Int(text, row.IncomeBonus);
|
||||||
WriteRosterList(text, row.Roster);
|
WriteRosterList(text, row.Roster);
|
||||||
Int(text, row.Hero != null ? 1 : 0);
|
Int(text, row.Hero != null ? 1 : 0);
|
||||||
if (row.Hero != null)
|
if (row.Hero != null)
|
||||||
|
|
@ -326,6 +330,7 @@ namespace Luneraria.Net
|
||||||
Str(text, hero.FreshItems != null ? string.Join("\x1d", hero.FreshItems) : "");
|
Str(text, hero.FreshItems != null ? string.Join("\x1d", hero.FreshItems) : "");
|
||||||
Str(text, hero.Ultimate ?? "");
|
Str(text, hero.Ultimate ?? "");
|
||||||
Str(text, hero.Skin ?? "");
|
Str(text, hero.Skin ?? "");
|
||||||
|
Int(text, hero.Exhausted ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteUnit(StringBuilder text, NetUnitRecord unit)
|
static void WriteUnit(StringBuilder text, NetUnitRecord unit)
|
||||||
|
|
@ -599,6 +604,7 @@ namespace Luneraria.Net
|
||||||
string profiles = Text();
|
string profiles = Text();
|
||||||
if (!string.IsNullOrEmpty(profiles))
|
if (!string.IsNullOrEmpty(profiles))
|
||||||
options.Profiles.AddRange(profiles.Split('\x1d'));
|
options.Profiles.AddRange(profiles.Split('\x1d'));
|
||||||
|
options.Extended = Integer() == 1;
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -650,6 +656,7 @@ namespace Luneraria.Net
|
||||||
Lane = Text() ?? ""
|
Lane = Text() ?? ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
state.Boss.Blessing = new[] { Integer(), Integer() };
|
||||||
int coreCount = Integer();
|
int coreCount = Integer();
|
||||||
for (int i = 0; i < coreCount; i++)
|
for (int i = 0; i < coreCount; i++)
|
||||||
state.DestroyedCores.Add(Integer());
|
state.DestroyedCores.Add(Integer());
|
||||||
|
|
@ -689,7 +696,8 @@ namespace Luneraria.Net
|
||||||
Earned = Integer(),
|
Earned = Integer(),
|
||||||
Spent = Integer(),
|
Spent = Integer(),
|
||||||
NextSeq = Integer(),
|
NextSeq = Integer(),
|
||||||
Undo = Integer()
|
Undo = Integer(),
|
||||||
|
IncomeBonus = Integer()
|
||||||
};
|
};
|
||||||
row.Roster = ReadRosterList();
|
row.Roster = ReadRosterList();
|
||||||
if (Flag())
|
if (Flag())
|
||||||
|
|
@ -762,6 +770,7 @@ namespace Luneraria.Net
|
||||||
AddSplit(hero.FreshItems, Text());
|
AddSplit(hero.FreshItems, Text());
|
||||||
hero.Ultimate = Text() ?? "";
|
hero.Ultimate = Text() ?? "";
|
||||||
hero.Skin = Text() ?? "";
|
hero.Skin = Text() ?? "";
|
||||||
|
hero.Exhausted = Integer() == 1;
|
||||||
return hero;
|
return hero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Luneraria.Core.Tests
|
||||||
|
|
||||||
public static IList<CheckSuite> All()
|
public static IList<CheckSuite> All()
|
||||||
{
|
{
|
||||||
return new List<CheckSuite> { new ContentSuite(), new CoreRulesSuite(), new M2RulesSuite(), new GoldenSuite(), new DeterminismSuite(), new NetSuite(), new NetRoundsSuite(), new H1HeroSuite() };
|
return new List<CheckSuite> { new ContentSuite(), new CoreRulesSuite(), new M2RulesSuite(), new GoldenSuite(), new DeterminismSuite(), new NetSuite(), new NetRoundsSuite(), new H1HeroSuite(), new S1RulesSuite() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
184
Assets/Luneraria/Tests/Core/S1RulesSuite.cs
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Luneraria.Core.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// S1: the extended rules after Legion TD 2, Dota 2 and League of Legends (MatchOptions.Extended).
|
||||||
|
/// Champion income, barracks, exhaustion and buyback, Mondsegen, the bot rotation with later
|
||||||
|
/// heroes, and that none of it acts under the reference rules.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class S1RulesSuite : CheckSuite
|
||||||
|
{
|
||||||
|
public override string Name { get { return "s1"; } }
|
||||||
|
|
||||||
|
protected override void RunChecks()
|
||||||
|
{
|
||||||
|
TestReferenceUntouched();
|
||||||
|
TestChampionIncome();
|
||||||
|
TestBarracks();
|
||||||
|
TestExhaustionAndBuyback();
|
||||||
|
TestBlessing();
|
||||||
|
TestBots();
|
||||||
|
}
|
||||||
|
|
||||||
|
static LuneMatch NewMatch(bool extended, int seed = 41)
|
||||||
|
{
|
||||||
|
var state = new LuneMatch();
|
||||||
|
state.Setup(new MatchOptions { Seed = seed, HumanSlot = -1, Extended = extended,
|
||||||
|
Bunds = new List<string> { "glutpakt", "wurzelbund", "glutpakt", "wurzelbund", "glutpakt", "wurzelbund" } });
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestReferenceUntouched()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(false);
|
||||||
|
Check(!state.Extended, "default options keep the reference rules");
|
||||||
|
state.Players[0].Draft.Gold += 1000;
|
||||||
|
state.Submit(0, Command.ChampionBook("ch_schutz", "top"));
|
||||||
|
state.Submit(0, Command.HeroBuy("held_ringborke"));
|
||||||
|
state.BeginCombat();
|
||||||
|
Check(state.Players[0].IncomeBonus == 0, "reference: a champion gives no income");
|
||||||
|
foreach (LuneUnit unit in state.Combat.Units.Where(u => u.Kind == "hero"))
|
||||||
|
unit.Hp = 0.0;
|
||||||
|
state.FinishCombat();
|
||||||
|
Check(!state.Players[0].Hero.Exhausted, "reference: a fallen hero is not exhausted");
|
||||||
|
Check(!state.Submit(0, Command.Buyback()).Ok, "reference: no buyback");
|
||||||
|
var setup = new LuneMatch();
|
||||||
|
setup.Setup(new MatchOptions { Seed = 3 });
|
||||||
|
Check(!setup.Extended, "MatchOptions.Extended defaults to false");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestChampionIncome()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
state.Players[0].Draft.Gold += 1000;
|
||||||
|
Check(state.Submit(0, Command.ChampionBook("ch_schutz", "top")).Ok, "book a champion");
|
||||||
|
Check(state.Players[0].IncomeBonus == 0, "income rises only when the booking is committed");
|
||||||
|
state.Submit(0, Command.ChampionCancel(state.Players[0].Draft.NewBookings[0].Uid));
|
||||||
|
state.Submit(0, Command.ChampionBook("ch_belagerung", "mid"));
|
||||||
|
state.BeginCombat();
|
||||||
|
Check(state.Players[0].IncomeBonus == 180 / 12, "Keilbrecher adds 15 income for good (" + state.Players[0].IncomeBonus + ")");
|
||||||
|
int before = state.Players[0].Gold;
|
||||||
|
int beforeOther = state.Players[1].Gold;
|
||||||
|
state.FinishCombat();
|
||||||
|
int gain = state.Players[0].Gold - before;
|
||||||
|
int gainOther = state.Players[1].Gold - beforeOther;
|
||||||
|
Check(gain - state.LastSettlement.Gains[0] == state.LastSettlement.Income + 15, "settlement pays round income plus the champion income");
|
||||||
|
Check(gainOther - state.LastSettlement.Gains[1] == state.LastSettlement.Income, "other seats get the plain income");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBarracks()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
Check(!state.BarracksBroken(0, "mid"), "barracks intact at the start");
|
||||||
|
state.BuildingHp["rot_mid_inner"] = 0.0;
|
||||||
|
Check(state.BarracksBroken(0, "mid") && !state.BarracksBroken(1, "mid") && !state.BarracksBroken(0, "top"), "Blau's mid waves are reinforced once Rot's inner mid tower fell");
|
||||||
|
state.BeginCombat();
|
||||||
|
List<LuneUnit> blueMid = state.Combat.Units.Where(u => u.Kind == "monster" && u.Team == 0 && u.Lane == "mid").ToList();
|
||||||
|
List<LuneUnit> blueTop = state.Combat.Units.Where(u => u.Kind == "monster" && u.Team == 0 && u.Lane == "top").ToList();
|
||||||
|
List<LuneUnit> redMid = state.Combat.Units.Where(u => u.Kind == "monster" && u.Team == 1 && u.Lane == "mid").ToList();
|
||||||
|
Check(blueMid.Count == blueTop.Count + 1 && redMid.Count == blueTop.Count, "one extra monster on the broken lane only");
|
||||||
|
Check(GdMath.IsEqualApprox(blueMid.Last().MaxHp, blueTop.Last().MaxHp * 1.25), "reinforced monsters have 125 % health");
|
||||||
|
LuneMatch reference = NewMatch(false);
|
||||||
|
reference.BuildingHp["rot_mid_inner"] = 0.0;
|
||||||
|
reference.BeginCombat();
|
||||||
|
Check(reference.Combat.Units.Count(u => u.Kind == "monster" && u.Team == 0 && u.Lane == "mid") == blueTop.Count, "reference: no barracks effect");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestExhaustionAndBuyback()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
state.Players[1].Draft.Gold += 2000;
|
||||||
|
state.Submit(1, Command.HeroBuy("held_sylvara"));
|
||||||
|
Check(!state.Submit(1, Command.Buyback()).Ok, "no buyback for a fresh hero");
|
||||||
|
state.BeginCombat();
|
||||||
|
LuneUnit hero = state.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == 1);
|
||||||
|
hero.Hp = 0.0;
|
||||||
|
state.FinishCombat();
|
||||||
|
HeroState committed = state.Players[1].Hero;
|
||||||
|
Check(committed.Exhausted, "a hero that fell is exhausted");
|
||||||
|
state.BeginCombat();
|
||||||
|
hero = state.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == 1);
|
||||||
|
Check(GdMath.IsEqualApprox(hero.Hp, hero.MaxHp * 0.6), "an exhausted hero starts with 60 % health");
|
||||||
|
state.FinishCombat();
|
||||||
|
Check(!state.Players[1].Hero.Exhausted || !state.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == 1).Alive, "surviving clears the exhaustion");
|
||||||
|
state.Players[1].Hero.Exhausted = true;
|
||||||
|
state.Players[1].Draft = state.Snapshot(state.Players[1]);
|
||||||
|
int level = LuneContent.LevelFor(state.Players[1].Hero.Xp);
|
||||||
|
int cost = 60 + 20 * level;
|
||||||
|
int gold = state.GoldOf(1);
|
||||||
|
Check(state.Submit(1, Command.Buyback()).Ok && state.GoldOf(1) == gold - cost, "buyback costs 60 + 20 × level (" + cost + ")");
|
||||||
|
Check(state.Undo(1).Ok && state.GoldOf(1) == gold && state.Players[1].Draft.Hero.Exhausted, "buyback can be undone");
|
||||||
|
state.Submit(1, Command.Buyback());
|
||||||
|
state.BeginCombat();
|
||||||
|
hero = state.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == 1);
|
||||||
|
Check(hero.Hp == hero.MaxHp, "a bought back hero starts at full health");
|
||||||
|
state.FinishCombat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBlessing()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
state.Players[0].Draft.Gold += 1000;
|
||||||
|
state.Submit(0, Command.Buy("g01", "top"));
|
||||||
|
state.Submit(0, Command.HeroBuy("held_funkenlanze"));
|
||||||
|
state.BeginCombat();
|
||||||
|
state.Combat.GrottoOutcome = "success";
|
||||||
|
state.Combat.GrottoClaimTeam = 0;
|
||||||
|
state.SettleBoss();
|
||||||
|
Check(state.Boss.Blessing[0] == 1 && state.Boss.Blessing[1] == 0, "a grotto victory blesses the winning team");
|
||||||
|
state.FinishCombat();
|
||||||
|
state.Boss.Blessing[0] = 2;
|
||||||
|
state.BeginCombat();
|
||||||
|
LuneUnit troop = state.Combat.Units.First(u => u.Kind == "troop" && u.OwnerSlot == 0);
|
||||||
|
Check(GdMath.IsEqualApprox(troop.MaxHp, DataCatalog.GetUnit("g01").MaxHp * 1.08), "two stacks: troops have 108 % health");
|
||||||
|
LuneUnit enemy = state.Combat.Units.FirstOrDefault(u => u.Kind == "troop" && u.Team == 1);
|
||||||
|
Check(enemy == null || GdMath.IsEqualApprox(enemy.MaxHp, DataCatalog.GetUnit(enemy.Def.Id).MaxHp), "the other team is not blessed");
|
||||||
|
state.FinishCombat();
|
||||||
|
state.Boss.Blessing[0] = 4;
|
||||||
|
state.BeginCombat();
|
||||||
|
LuneUnit hero = state.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == 0);
|
||||||
|
Check(GdMath.IsEqualApprox(hero.Def.Regen, 0.01), "Mondseele: heroes regenerate 1 % per second");
|
||||||
|
state.FinishCombat();
|
||||||
|
state.Combat.GrottoOutcome = "success";
|
||||||
|
state.Combat.GrottoClaimTeam = 0;
|
||||||
|
state.Boss.SettledRound = 0;
|
||||||
|
state.Boss.Claim = null;
|
||||||
|
state.Boss.NextAvailableRound = 1;
|
||||||
|
state.SettleBoss();
|
||||||
|
Check(state.Boss.Blessing[0] == 4, "Mondsegen stops at four stacks");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBots()
|
||||||
|
{
|
||||||
|
bool sylvaraPicked = false;
|
||||||
|
for (int seed = 1; seed <= 20 && !sylvaraPicked; seed++)
|
||||||
|
{
|
||||||
|
LuneMatch probe = NewMatch(true, seed);
|
||||||
|
for (int slot = 0; slot < 6; slot++)
|
||||||
|
sylvaraPicked |= LuneBot.ProfileFor(probe, slot) == "assassine";
|
||||||
|
}
|
||||||
|
Check(sylvaraPicked, "extended rules: the bot rotation includes Sylvara's profile");
|
||||||
|
string first = Play(17, out LuneMatch played);
|
||||||
|
Check(first == Play(17, out LuneMatch _), "extended bot matches are deterministic");
|
||||||
|
Check(played.Phase == Phase.Over, "an extended bot match ends by an Ancient (" + first.Split('|')[0] + ")");
|
||||||
|
Check(played.Players.Any(p => p.IncomeBonus > 0), "bots earn champion income");
|
||||||
|
Note("extended seed 17: " + first.Split('|')[0] + ", income bonus " + string.Join(",", played.Players.Select(p => p.IncomeBonus)) + ", blessing " + played.Boss.Blessing[0] + "/" + played.Boss.Blessing[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static string Play(int seed, out LuneMatch match)
|
||||||
|
{
|
||||||
|
match = NewMatch(true, seed);
|
||||||
|
while (match.Phase != Phase.Over && match.RoundNumber <= 80)
|
||||||
|
{
|
||||||
|
for (int slot = 0; slot < 6; slot++)
|
||||||
|
LuneBot.Plan(match, slot);
|
||||||
|
match.BeginCombat();
|
||||||
|
match.FinishCombat();
|
||||||
|
}
|
||||||
|
return match.RoundNumber + "/" + match.Winner + "|" + string.Join(",", match.Players.Select(p => p.Gold + ":" + p.Earned + ":" + p.IncomeBonus))
|
||||||
|
+ "|" + string.Join(",", match.BuildingHp.Values.Select(v => v.ToString("R", System.Globalization.CultureInfo.InvariantCulture)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Luneraria/Tests/Core/S1RulesSuite.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9e62ba3250254f8ba4fee06c56dfec7d
|
||||||
|
|
@ -52,5 +52,6 @@ namespace Luneraria.Core.Tests
|
||||||
[Test] public void NetProtocol() { RunSuite(new NetSuite()); }
|
[Test] public void NetProtocol() { RunSuite(new NetSuite()); }
|
||||||
[Test] public void NetRounds() { RunSuite(new NetRoundsSuite()); }
|
[Test] public void NetRounds() { RunSuite(new NetRoundsSuite()); }
|
||||||
[Test] public void H1Heroes() { RunSuite(new H1HeroSuite()); }
|
[Test] public void H1Heroes() { RunSuite(new H1HeroSuite()); }
|
||||||
|
[Test] public void S1Rules() { RunSuite(new S1RulesSuite()); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Luneraria.UI.Tests
|
||||||
public static string Root { get { return Path.GetDirectoryName(Application.dataPath); } }
|
public static string Root { get { return Path.GetDirectoryName(Application.dataPath); } }
|
||||||
|
|
||||||
/// <summary>Loads the scene and starts a match with the human seat, HUD into a width×height texture.</summary>
|
/// <summary>Loads the scene and starts a match with the human seat, HUD into a width×height texture.</summary>
|
||||||
public IEnumerator Open(int width = 1920, int height = 1080, int seed = 4, float uiScale = 1f, bool reducedMotion = false, int startRound = 1, int extraGold = 0, string[] bunds = null)
|
public IEnumerator Open(int width = 1920, int height = 1080, int seed = 4, float uiScale = 1f, bool reducedMotion = false, int startRound = 1, int extraGold = 0, string[] bunds = null, bool extended = false)
|
||||||
{
|
{
|
||||||
LunePreferences.ResetSession(uiScale, reducedMotion);
|
LunePreferences.ResetSession(uiScale, reducedMotion);
|
||||||
yield return HudCaptureTests.LoadScene();
|
yield return HudCaptureTests.LoadScene();
|
||||||
|
|
@ -62,6 +62,7 @@ namespace Luneraria.UI.Tests
|
||||||
Driver.StartRound = startRound;
|
Driver.StartRound = startRound;
|
||||||
Driver.ExtraGold = extraGold;
|
Driver.ExtraGold = extraGold;
|
||||||
Driver.Bunds = bunds;
|
Driver.Bunds = bunds;
|
||||||
|
Driver.Extended = extended;
|
||||||
Driver.Speed = 1f;
|
Driver.Speed = 1f;
|
||||||
Driver.Restart();
|
Driver.Restart();
|
||||||
yield return Frames(3);
|
yield return Frames(3);
|
||||||
|
|
|
||||||
120
Assets/Luneraria/Tests/UI/PlayMode/S1RulesViewsTests.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Linq;
|
||||||
|
using Luneraria.Core;
|
||||||
|
using Luneraria.View;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.TestTools;
|
||||||
|
|
||||||
|
namespace Luneraria.UI.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// S1: the extended rules in the real scene. S1Hud checks the champion income in the gold display and the champion tooltip, the barracks mark of a
|
||||||
|
/// lane, exhaustion and the buyback button of the hero window, and the Mondsegen in the team
|
||||||
|
/// header. Fixtures set public match state only (a fallen tower, a fallen hero, blessing
|
||||||
|
/// stacks), as the G3 verify does. S1Bilder (explicit) captures the evidence at 1920×1080;
|
||||||
|
/// LUNE_EVIDENCE names the folder below docs/port/screenshots (default s1/spiel).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class S1RulesViewsTests
|
||||||
|
{
|
||||||
|
static void Send(HudSession s, Command command)
|
||||||
|
{
|
||||||
|
CommandResult result = s.Match.Submit(HudSession.Human, command);
|
||||||
|
Assert.IsTrue(result.Ok, command.Type + ": " + result.Message);
|
||||||
|
s.Game.RebuildPreview();
|
||||||
|
s.Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
static IEnumerator WaitReady(HudSession s)
|
||||||
|
{
|
||||||
|
float waited = 0f;
|
||||||
|
while (s.Hud.ReadyBlocked() && waited < 5f)
|
||||||
|
{
|
||||||
|
waited += Time.unscaledDeltaTime;
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>One round with a booked champion and a hero that falls; returns in the next planning.</summary>
|
||||||
|
static IEnumerator Prepare(HudSession s)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(s.Match.Extended, "the driver started an extended match");
|
||||||
|
yield return s.Starter();
|
||||||
|
s.Leave();
|
||||||
|
Send(s, Command.HeroBuy("held_sylvara"));
|
||||||
|
Send(s, Command.ChampionBook("ch_belagerung", "mid"));
|
||||||
|
s.Game.Key(KeyCode.Space);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
Assert.AreEqual(LuneMatchRunner.Stage.Combat, s.Runner.Current, "combat starts");
|
||||||
|
LuneUnit hero = s.Match.Combat.Units.First(u => u.Kind == "hero" && u.OwnerSlot == HudSession.Human);
|
||||||
|
hero.Hp = 0.0;
|
||||||
|
yield return s.FinishCombat();
|
||||||
|
yield return WaitReady(s);
|
||||||
|
// Public fixtures: Rot's inner mid tower fell earlier, Blau won the grotto twice.
|
||||||
|
s.Match.BuildingHp["rot_mid_inner"] = 0.0;
|
||||||
|
s.Match.BuildingHp["rot_mid_outer"] = 0.0;
|
||||||
|
s.Match.Boss.Blessing[0] = 2;
|
||||||
|
s.Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest, Timeout(600000)]
|
||||||
|
public IEnumerator S1Hud()
|
||||||
|
{
|
||||||
|
var s = new HudSession();
|
||||||
|
yield return s.Open(1920, 1080, seed: 4, startRound: 6, extraGold: 1500, extended: true);
|
||||||
|
yield return Prepare(s);
|
||||||
|
Assert.AreEqual(15, s.Me.IncomeBonus, "the Keilbrecher adds 15 income");
|
||||||
|
string texts = s.Texts();
|
||||||
|
StringAssert.Contains("davon 15 Söldner", texts, "the gold display shows the champion income");
|
||||||
|
StringAssert.Contains("SEGEN 2/4", texts, "the team header shows the Mondsegen");
|
||||||
|
Assert.IsTrue(HudSession.Visible(s.Hud.Lanes["mid"].Barracks), "mid shows the barracks mark");
|
||||||
|
Assert.IsFalse(HudSession.Visible(s.Hud.Lanes["top"].Barracks), "top shows none");
|
||||||
|
Assert.IsTrue(s.Me.Hero.Exhausted, "the fallen hero is exhausted");
|
||||||
|
s.Hud.OpenHero();
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
Assert.IsNotNull(s.Hud.HeroDetail.BuybackButton, "the hero window offers the buyback");
|
||||||
|
int gold = s.Me.Draft.Gold;
|
||||||
|
int cost = s.Match.BuybackCost(s.Me.Draft.Hero);
|
||||||
|
s.Click(s.Hud.HeroDetail.BuybackButton);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
Assert.AreEqual(gold - cost, s.Me.Draft.Gold, "the buyback costs " + cost);
|
||||||
|
Assert.IsFalse(s.Me.Draft.Hero.Exhausted, "the hero is bought back");
|
||||||
|
Assert.IsNull(s.Hud.HeroDetail.BuybackButton, "no buyback button afterwards");
|
||||||
|
s.Hud.CloseModal();
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest, Timeout(600000), Explicit("Bildwerkzeug: nur gezielt per -testFilter")]
|
||||||
|
public IEnumerator S1Bilder()
|
||||||
|
{
|
||||||
|
string evidence = Environment.GetEnvironmentVariable("LUNE_EVIDENCE");
|
||||||
|
string folder = "docs/port/screenshots/" + (string.IsNullOrEmpty(evidence) ? "s1/spiel" : evidence) + "/";
|
||||||
|
var s = new HudSession();
|
||||||
|
yield return s.Open(1920, 1080, seed: 4, startRound: 6, extraGold: 1500, extended: true);
|
||||||
|
yield return Prepare(s);
|
||||||
|
yield return new WaitForSecondsRealtime(.5f);
|
||||||
|
yield return s.Shot(folder + "01-planung-einkommen-kaserne-mondsegen.png");
|
||||||
|
s.Hover(s.Hud.Lanes["mid"]);
|
||||||
|
yield return new WaitForSecondsRealtime(.3f);
|
||||||
|
yield return s.Shot(folder + "02-kaserne-tooltip.png");
|
||||||
|
s.Leave();
|
||||||
|
s.Hud.OpenHero();
|
||||||
|
yield return new WaitForSecondsRealtime(.6f);
|
||||||
|
yield return s.Shot(folder + "03-rueckkauf.png");
|
||||||
|
s.Hud.CloseModal();
|
||||||
|
s.Game.Key(KeyCode.Space);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
yield return s.Fight(12.0);
|
||||||
|
s.Driver.Paused = true;
|
||||||
|
LuneUnit monster = s.Match.Combat.Units.Where(u => u.Kind == "monster" && u.Team == 0 && u.Lane == "mid" && u.Alive).OrderByDescending(u => u.Progress).FirstOrDefault();
|
||||||
|
if (monster != null)
|
||||||
|
s.Rig.LookAt(monster.Position, 20f, true);
|
||||||
|
yield return new WaitForSecondsRealtime(.5f);
|
||||||
|
yield return s.Shot(folder + "04-verstaerkte-welle-mid.png");
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1591de50998e4ea096591b6bcb4464c2
|
||||||
|
|
@ -30,6 +30,8 @@ namespace Luneraria.UI
|
||||||
LuneButton _laneTop;
|
LuneButton _laneTop;
|
||||||
LuneButton _laneMid;
|
LuneButton _laneMid;
|
||||||
LuneButton _laneBot;
|
LuneButton _laneBot;
|
||||||
|
/// <summary>S1: the rules toggle (extended or reference rules).</summary>
|
||||||
|
public LuneButton RulesToggle;
|
||||||
|
|
||||||
public LuneMainMenu()
|
public LuneMainMenu()
|
||||||
{
|
{
|
||||||
|
|
@ -78,6 +80,18 @@ namespace Luneraria.UI
|
||||||
_laneBot.Clicked += () => SetLane("bot");
|
_laneBot.Clicked += () => SetLane("bot");
|
||||||
SetLane("mid");
|
SetLane("mid");
|
||||||
|
|
||||||
|
LuneUi.Label("REGELN", "SectionLabel", column);
|
||||||
|
RulesToggle = new LuneButton("", LuneButtons.HudToggle, column);
|
||||||
|
RulesToggle.ToggleMode = true;
|
||||||
|
RulesToggle.Clicked += () => SetRules(!LunePreferences.ExtendedRules);
|
||||||
|
LuneTooltip.Attach(RulesToggle, () => new LuneTip
|
||||||
|
{
|
||||||
|
Title = "Erweiterte Regeln (S1)", Glyph = "star", GlyphColor = LuneUiTheme.Hero,
|
||||||
|
Text = "Söldner-Einkommen wie in Legion TD 2: Angriffschampions erhöhen dauerhaft dein Einkommen.\nKasernen wie in Dota 2: Fällt ein innerer Turm, kommen die gegnerischen Wellen dort verstärkt.\nRückkauf: Gefallene Helden starten erschöpft, außer du kaufst sie zurück.\nMondsegen wie die Drachenseelen in League of Legends: Jeder Bosssieg stärkt dein Team dauerhaft.\nBots wählen auch Sylvara.",
|
||||||
|
Note = "Aus: die Referenzregeln des Ports.",
|
||||||
|
});
|
||||||
|
SetRules(LunePreferences.ExtendedRules);
|
||||||
|
|
||||||
LuneUi.Label("NETZWERK · DIREKTVERBINDUNG", "SectionLabel", column);
|
LuneUi.Label("NETZWERK · DIREKTVERBINDUNG", "SectionLabel", column);
|
||||||
_name = Field(column, "Anzeigename", "Spieler");
|
_name = Field(column, "Anzeigename", "Spieler");
|
||||||
VisualElement netRow = LuneUi.Box(false, 10f, column);
|
VisualElement netRow = LuneUi.Box(false, 10f, column);
|
||||||
|
|
@ -116,6 +130,13 @@ namespace Luneraria.UI
|
||||||
_bundWurzel.On = bund == "wurzelbund";
|
_bundWurzel.On = bund == "wurzelbund";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetRules(bool extended)
|
||||||
|
{
|
||||||
|
LunePreferences.ExtendedRules = extended;
|
||||||
|
RulesToggle.On = extended;
|
||||||
|
RulesToggle.Text = extended ? "ERWEITERT · Legion TD 2, Dota 2, LoL" : "REFERENZ · Regeln des Ports";
|
||||||
|
}
|
||||||
|
|
||||||
void SetLane(string lane)
|
void SetLane(string lane)
|
||||||
{
|
{
|
||||||
Lane = lane;
|
Lane = lane;
|
||||||
|
|
@ -130,7 +151,7 @@ namespace Luneraria.UI
|
||||||
int seat = Array.IndexOf(LuneMap.Lanes, Lane);
|
int seat = Array.IndexOf(LuneMap.Lanes, Lane);
|
||||||
if (seat < 0)
|
if (seat < 0)
|
||||||
seat = 1;
|
seat = 1;
|
||||||
var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = seat };
|
var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = seat, Extended = LunePreferences.ExtendedRules };
|
||||||
for (int slot = 0; slot < 6; slot++)
|
for (int slot = 0; slot < 6; slot++)
|
||||||
options.Bunds.Add(slot == seat ? Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]);
|
options.Bunds.Add(slot == seat ? Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]);
|
||||||
return options;
|
return options;
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ namespace Luneraria.UI
|
||||||
ResetSession();
|
ResetSession();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = 0 };
|
var options = new MatchOptions { Seed = Environment.TickCount & 0xffff, HumanSlot = 0, Extended = LunePreferences.ExtendedRules };
|
||||||
for (int slot = 0; slot < 6; slot++)
|
for (int slot = 0; slot < 6; slot++)
|
||||||
options.Bunds.Add(slot == 0 ? Menu.Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]);
|
options.Bunds.Add(slot == 0 ? Menu.Bund : DataCatalog.Factions[(slot + options.Seed) % DataCatalog.Factions.Length]);
|
||||||
if (!Session.HostNetwork(options, displayName))
|
if (!Session.HostNetwork(options, displayName))
|
||||||
|
|
@ -213,6 +213,7 @@ namespace Luneraria.UI
|
||||||
Driver.HumanSlot = Game.HumanSlot;
|
Driver.HumanSlot = Game.HumanSlot;
|
||||||
Driver.Bunds = _lastOptions != null && _lastOptions.Bunds != null ? _lastOptions.Bunds.ToArray() : null;
|
Driver.Bunds = _lastOptions != null && _lastOptions.Bunds != null ? _lastOptions.Bunds.ToArray() : null;
|
||||||
Driver.Seed = _lastOptions != null ? _lastOptions.Seed : Driver.Seed;
|
Driver.Seed = _lastOptions != null ? _lastOptions.Seed : Driver.Seed;
|
||||||
|
Driver.Extended = _lastOptions != null && _lastOptions.Extended;
|
||||||
Driver.Restart();
|
Driver.Restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,8 @@ namespace Luneraria.UI
|
||||||
{
|
{
|
||||||
Title = spec.Name + " · Angriffschampion", Glyph = "champion", GlyphColor = LuneUiTheme.Champion,
|
Title = spec.Name + " · Angriffschampion", Glyph = "champion", GlyphColor = LuneUiTheme.Champion,
|
||||||
Subtitle = spec.RoleText + " · " + spec.Cost + " Gold · Einsatz in Welle " + round + " – " + LuneUi.Upper(LuneMap.LaneNames[TargetLane]),
|
Subtitle = spec.RoleText + " · " + spec.Cost + " Gold · Einsatz in Welle " + round + " – " + LuneUi.Upper(LuneMap.LaneNames[TargetLane]),
|
||||||
Text = spec.Text,
|
// S1 (Legion TD 2): sending is also an investment in income.
|
||||||
|
Text = spec.Text + (_model != null && _model.State.Extended ? "\nSöldner-Einkommen: +" + _model.State.ChampionIncome(spec.Cost) + " Gold in jeder folgenden Runde, dauerhaft." : ""),
|
||||||
Note = "Einmaliger Einsatz: kämpft eine Welle lang und verschwindet danach. Die Ankündigung ist für alle sichtbar; sein Tod bringt dem Gegner " + definition.Bounty + " Gold.",
|
Note = "Einmaliger Einsatz: kämpft eine Welle lang und verschwindet danach. Die Ankündigung ist für alle sichtbar; sein Tod bringt dem Gegner " + definition.Bounty + " Gold.",
|
||||||
Warn = Cards[championId].Reason,
|
Warn = Cards[championId].Reason,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Luneraria.UI
|
||||||
Detail = LuneUi.Label("", "HudMuted", column);
|
Detail = LuneUi.Label("", "HudMuted", column);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetValues(int gold, int income, bool showIncome = true)
|
public void SetValues(int gold, int income, bool showIncome = true, int bonus = 0)
|
||||||
{
|
{
|
||||||
if (gold != _shown)
|
if (gold != _shown)
|
||||||
{
|
{
|
||||||
|
|
@ -75,7 +75,8 @@ namespace Luneraria.UI
|
||||||
_shown = gold;
|
_shown = gold;
|
||||||
Amount.text = LuneUiTheme.Number(gold);
|
Amount.text = LuneUiTheme.Number(gold);
|
||||||
}
|
}
|
||||||
string line = showIncome ? "+" + LuneUiTheme.Number(income) + " nächste Runde" : "";
|
// S1: the champion income is part of the sum; the short form fits the gold column.
|
||||||
|
string line = !showIncome ? "" : bonus > 0 ? "+" + LuneUiTheme.Number(income) + " · davon " + bonus + " Söldner" : "+" + LuneUiTheme.Number(income) + " nächste Runde";
|
||||||
if (Detail.text != line)
|
if (Detail.text != line)
|
||||||
Detail.text = line;
|
Detail.text = line;
|
||||||
LuneUi.Show(Detail, showIncome);
|
LuneUi.Show(Detail, showIncome);
|
||||||
|
|
@ -146,15 +147,18 @@ namespace Luneraria.UI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>S1: Mondsegen stacks of the own team (-1 under the reference rules).</summary>
|
||||||
|
public int Blessing = -1;
|
||||||
|
|
||||||
public void Apply(List<LuneHudModel.MemberInfo> members, int team, bool planning, bool networked)
|
public void Apply(List<LuneHudModel.MemberInfo> members, int team, bool planning, bool networked)
|
||||||
{
|
{
|
||||||
string signature = string.Join(",", members) + "|" + team + "|" + planning + "|" + networked;
|
string signature = string.Join(",", members) + "|" + team + "|" + planning + "|" + networked + "|" + Blessing;
|
||||||
if (signature == _signature)
|
if (signature == _signature)
|
||||||
return;
|
return;
|
||||||
_signature = signature;
|
_signature = signature;
|
||||||
_emblem.Glyph = "emblem_" + team;
|
_emblem.Glyph = "emblem_" + team;
|
||||||
_emblem.Color = LuneUiTheme.TeamColor(team);
|
_emblem.Color = LuneUiTheme.TeamColor(team);
|
||||||
_header.text = "DEIN TEAM · " + LuneUi.Upper(LuneUiTheme.TeamName(team));
|
_header.text = "DEIN TEAM · " + LuneUi.Upper(LuneUiTheme.TeamName(team)) + (Blessing > 0 ? " · SEGEN " + Blessing + "/4" : "");
|
||||||
for (int index = 0; index < Mathf.Min(3, members.Count); index++)
|
for (int index = 0; index < Mathf.Min(3, members.Count); index++)
|
||||||
{
|
{
|
||||||
LuneHudModel.MemberInfo member = members[index];
|
LuneHudModel.MemberInfo member = members[index];
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ namespace Luneraria.UI
|
||||||
_hpBar.Fill = LuneHpBar.HealthColor(_hpBar.Fraction, LuneUiTheme.Good);
|
_hpBar.Fill = LuneHpBar.HealthColor(_hpBar.Fraction, LuneUiTheme.Good);
|
||||||
_portrait.Crossed = !live.Alive;
|
_portrait.Crossed = !live.Alive;
|
||||||
}
|
}
|
||||||
string signature = (hasHero ? hero.HeroId + ":" + hero.Xp + ":" + hero.Lane + ":" + string.Join(",", hero.Items) + ":" + string.Join(",", hero.Talents) + ":" + hero.Ultimate + ":" + hero.Skin : "-") + "|"
|
string signature = (hasHero ? hero.HeroId + ":" + hero.Xp + ":" + hero.Lane + ":" + string.Join(",", hero.Items) + ":" + string.Join(",", hero.Talents) + ":" + hero.Ultimate + ":" + hero.Skin + ":" + hero.Exhausted : "-") + "|"
|
||||||
+ (live != null) + "|" + (live != null ? live.UltimateSerial : 0) + "|" + model.OwnGold + "|" + model.Planning;
|
+ (live != null) + "|" + (live != null ? live.UltimateSerial : 0) + "|" + model.OwnGold + "|" + model.Planning;
|
||||||
if (signature == _signature)
|
if (signature == _signature)
|
||||||
return;
|
return;
|
||||||
|
|
@ -188,7 +188,7 @@ namespace Luneraria.UI
|
||||||
_xpText.text = top ? "EP " + hero.Xp + " · max" : "EP " + hero.Xp + " / " + high;
|
_xpText.text = top ? "EP " + hero.Xp + " · max" : "EP " + hero.Xp + " / " + high;
|
||||||
LuneUi.Show(_hpBar, live != null);
|
LuneUi.Show(_hpBar, live != null);
|
||||||
if (live == null)
|
if (live == null)
|
||||||
_hp.text = LuneUiTheme.Number(_definition.MaxHp) + " Leben";
|
_hp.text = LuneUiTheme.Number(_definition.MaxHp) + " Leben" + (hero.Exhausted && model.State.Extended ? " · ERSCHÖPFT" : "");
|
||||||
for (int index = 0; index < _slots.Length; index++)
|
for (int index = 0; index < _slots.Length; index++)
|
||||||
{
|
{
|
||||||
string itemId = index < hero.Items.Count ? hero.Items[index] : "";
|
string itemId = index < hero.Items.Count ? hero.Items[index] : "";
|
||||||
|
|
@ -269,6 +269,8 @@ namespace Luneraria.UI
|
||||||
public readonly List<LuneButton> TalentCards = new List<LuneButton>();
|
public readonly List<LuneButton> TalentCards = new List<LuneButton>();
|
||||||
public readonly Dictionary<string, LuneButton> RuleCards = new Dictionary<string, LuneButton>();
|
public readonly Dictionary<string, LuneButton> RuleCards = new Dictionary<string, LuneButton>();
|
||||||
public readonly Dictionary<string, LuneButton> SkinCards = new Dictionary<string, LuneButton>();
|
public readonly Dictionary<string, LuneButton> SkinCards = new Dictionary<string, LuneButton>();
|
||||||
|
/// <summary>S1: buys an exhausted hero back (null when not offered).</summary>
|
||||||
|
public LuneButton BuybackButton;
|
||||||
public LuneTabBar Tabs;
|
public LuneTabBar Tabs;
|
||||||
LuneHudModel _model;
|
LuneHudModel _model;
|
||||||
string _signature = "";
|
string _signature = "";
|
||||||
|
|
@ -297,7 +299,7 @@ namespace Luneraria.UI
|
||||||
{
|
{
|
||||||
_model = model;
|
_model = model;
|
||||||
HeroState hero = model.OwnHero;
|
HeroState hero = model.OwnHero;
|
||||||
string signature = (hero != null ? hero.HeroId + ":" + hero.Xp + ":" + hero.Lane + ":" + hero.Fresh + ":" + string.Join(",", hero.Items) + ":" + string.Join(",", hero.Talents) + ":" + string.Join(",", hero.FreshItems) + ":" + hero.Ultimate + ":" + hero.Skin : "-")
|
string signature = (hero != null ? hero.HeroId + ":" + hero.Xp + ":" + hero.Lane + ":" + hero.Fresh + ":" + string.Join(",", hero.Items) + ":" + string.Join(",", hero.Talents) + ":" + string.Join(",", hero.FreshItems) + ":" + hero.Ultimate + ":" + hero.Skin + ":" + hero.Exhausted : "-")
|
||||||
+ "|" + model.OwnGold + "|" + model.Planning + "|" + model.Locked + "|" + Tab + "|" + model.State.GrottoAvailable() + "|" + model.State.RoundNumber;
|
+ "|" + model.OwnGold + "|" + model.Planning + "|" + model.Locked + "|" + Tab + "|" + model.State.GrottoAvailable() + "|" + model.State.RoundNumber;
|
||||||
if (signature == _signature)
|
if (signature == _signature)
|
||||||
return;
|
return;
|
||||||
|
|
@ -309,6 +311,7 @@ namespace Luneraria.UI
|
||||||
TalentCards.Clear();
|
TalentCards.Clear();
|
||||||
RuleCards.Clear();
|
RuleCards.Clear();
|
||||||
SkinCards.Clear();
|
SkinCards.Clear();
|
||||||
|
BuybackButton = null;
|
||||||
Tabs = null;
|
Tabs = null;
|
||||||
if (hero == null)
|
if (hero == null)
|
||||||
Recruit(model);
|
Recruit(model);
|
||||||
|
|
@ -428,6 +431,16 @@ namespace Luneraria.UI
|
||||||
}
|
}
|
||||||
if (hero.Lane == LuneMatch.Grotto)
|
if (hero.Lane == LuneMatch.Grotto)
|
||||||
LuneUi.SetColor(LuneUi.Wrap(LuneUi.Label("Der Held fehlt seiner Lane in der nächsten Kampfphase.", "HudText", left)), LuneUiTheme.Warn);
|
LuneUi.SetColor(LuneUi.Wrap(LuneUi.Label("Der Held fehlt seiner Lane in der nächsten Kampfphase.", "HudText", left)), LuneUiTheme.Warn);
|
||||||
|
// S1 (Dota 2 buyback, adapted): a fallen hero starts exhausted unless bought back.
|
||||||
|
if (hero.Exhausted && model.State.Extended)
|
||||||
|
{
|
||||||
|
int percent = (int)Math.Round(model.State.Balance.ExhaustedHp * 100.0);
|
||||||
|
LuneUi.SetColor(LuneUi.Wrap(LuneUi.Label("ERSCHÖPFT: gefallen, startet mit " + percent + " % Leben.", "HudText", left)), LuneUiTheme.Warn);
|
||||||
|
int cost = model.State.BuybackCost(hero);
|
||||||
|
BuybackButton = new LuneButton("Rückkauf · " + cost + " G", LuneButtons.HudPrimary, left);
|
||||||
|
BuybackButton.Disabled = !canAct || model.OwnGold < cost;
|
||||||
|
BuybackButton.Clicked += () => CommandRequested?.Invoke(Command.Buyback());
|
||||||
|
}
|
||||||
if (hero.Fresh && model.Planning)
|
if (hero.Fresh && model.Planning)
|
||||||
{
|
{
|
||||||
int refund = hero.Invested + hero.Items.Sum(id => LuneContent.Items[id].Cost);
|
int refund = hero.Invested + hero.Items.Sum(id => LuneContent.Items[id].Cost);
|
||||||
|
|
|
||||||
|
|
@ -797,6 +797,7 @@ namespace Luneraria.UI
|
||||||
bool reduced = LunePreferences.ReducedMotion;
|
bool reduced = LunePreferences.ReducedMotion;
|
||||||
foreach (KeyValuePair<string, LuneLaneIndicator> pair in Lanes)
|
foreach (KeyValuePair<string, LuneLaneIndicator> pair in Lanes)
|
||||||
pair.Value.Apply(Model.Lane(pair.Key), Model.Team, delta, reduced);
|
pair.Value.Apply(Model.Lane(pair.Key), Model.Team, delta, reduced);
|
||||||
|
_team.Blessing = Model.State.Extended ? Model.State.Boss.Blessing[Model.Team] : -1;
|
||||||
_team.Apply(Model.Members(Model.Team), Model.Team, planning, Model.Networked);
|
_team.Apply(Model.Members(Model.Team), Model.Team, planning, Model.Networked);
|
||||||
SyncRight(planning, fighting);
|
SyncRight(planning, fighting);
|
||||||
SyncBottom(planning, fighting);
|
SyncBottom(planning, fighting);
|
||||||
|
|
@ -900,7 +901,7 @@ namespace Luneraria.UI
|
||||||
|
|
||||||
void SyncBottom(bool planning, bool fighting)
|
void SyncBottom(bool planning, bool fighting)
|
||||||
{
|
{
|
||||||
_gold.SetValues(Model.OwnGold, Model.Income, !Model.Over);
|
_gold.SetValues(Model.OwnGold, Model.Income, !Model.Over, Model.IncomeBonus);
|
||||||
List<RosterEntry> roster = Model.OwnRoster;
|
List<RosterEntry> roster = Model.OwnRoster;
|
||||||
bool locked = Model.Locked;
|
bool locked = Model.Locked;
|
||||||
bool canAct = planning && !locked;
|
bool canAct = planning && !locked;
|
||||||
|
|
@ -1447,7 +1448,7 @@ namespace Luneraria.UI
|
||||||
Title = "Dein Gold", Glyph = "coin", GlyphColor = LuneUiTheme.Gold,
|
Title = "Dein Gold", Glyph = "coin", GlyphColor = LuneUiTheme.Gold,
|
||||||
Text = "Dazu kommt Gold für besiegte Monster, Champions, Gebäude und den Boss auf der eigenen Seite (Teile gehen ans Team).",
|
Text = "Dazu kommt Gold für besiegte Monster, Champions, Gebäude und den Boss auf der eigenen Seite (Teile gehen ans Team).",
|
||||||
Note = "Das Gold der anderen Spieler bleibt privat.",
|
Note = "Das Gold der anderen Spieler bleibt privat.",
|
||||||
}.Stat("coin", "Jetzt", LuneUiTheme.Number(Model.OwnGold)).Stat("clock", "Einkommen", "+" + LuneUiTheme.Number(Model.Income) + " nach dieser Welle");
|
}.Stat("coin", "Jetzt", LuneUiTheme.Number(Model.OwnGold)).Stat("clock", "Einkommen", "+" + LuneUiTheme.Number(Model.Income) + " nach dieser Welle" + (Model.IncomeBonus > 0 ? " (davon " + Model.IncomeBonus + " Söldner-Einkommen aus Angriffschampions)" : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
LuneTip StarterTip()
|
LuneTip StarterTip()
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ namespace Luneraria.UI
|
||||||
readonly LuneGlyphElement _mine;
|
readonly LuneGlyphElement _mine;
|
||||||
readonly LuneGlyphElement _ownShape;
|
readonly LuneGlyphElement _ownShape;
|
||||||
readonly LuneGlyphElement _enemyShape;
|
readonly LuneGlyphElement _enemyShape;
|
||||||
|
/// <summary>S1 barracks: crossed swords in the own team colour (own waves reinforced) or red (enemy waves).</summary>
|
||||||
|
public readonly LuneGlyphElement Barracks;
|
||||||
float _lowerHold;
|
float _lowerHold;
|
||||||
float _pulse;
|
float _pulse;
|
||||||
string _signature = "";
|
string _signature = "";
|
||||||
|
|
@ -57,6 +59,8 @@ namespace Luneraria.UI
|
||||||
_mine = LuneUi.Glyph("star", LuneUiTheme.BronzeLight, 12f, head);
|
_mine = LuneUi.Glyph("star", LuneUiTheme.BronzeLight, 12f, head);
|
||||||
LuneUi.Show(_mine, false);
|
LuneUi.Show(_mine, false);
|
||||||
LuneUi.Spacer(head);
|
LuneUi.Spacer(head);
|
||||||
|
Barracks = LuneUi.Glyph("melee", LuneUiTheme.Muted, 14f, head);
|
||||||
|
LuneUi.Show(Barracks, false);
|
||||||
_badge = new LuneStatusBadge();
|
_badge = new LuneStatusBadge();
|
||||||
head.Add(_badge);
|
head.Add(_badge);
|
||||||
VisualElement chain = LuneUi.Box(false, 3f, column);
|
VisualElement chain = LuneUi.Box(false, 3f, column);
|
||||||
|
|
@ -121,6 +125,8 @@ namespace Luneraria.UI
|
||||||
}
|
}
|
||||||
Color tint = LuneUiTheme.Threat[Level];
|
Color tint = LuneUiTheme.Threat[Level];
|
||||||
_badge.SetState(LuneLaneThreat.Names[Level], tint, Level >= 2 ? LuneLaneThreat.Glyphs[Level] : "", Level == LuneLaneThreat.Critical);
|
_badge.SetState(LuneLaneThreat.Names[Level], tint, Level >= 2 ? LuneLaneThreat.Glyphs[Level] : "", Level == LuneLaneThreat.Critical);
|
||||||
|
LuneUi.Show(Barracks, entry.OwnBarracks || entry.EnemyBarracks);
|
||||||
|
Barracks.Color = entry.EnemyBarracks ? LuneUiTheme.Danger : LuneUiTheme.TeamColor(ownTeam);
|
||||||
_ownShape.Glyph = "emblem_" + ownTeam;
|
_ownShape.Glyph = "emblem_" + ownTeam;
|
||||||
_ownShape.Color = LuneUiTheme.TeamColor(ownTeam);
|
_ownShape.Color = LuneUiTheme.TeamColor(ownTeam);
|
||||||
_enemyShape.Glyph = "emblem_" + (1 - ownTeam);
|
_enemyShape.Glyph = "emblem_" + (1 - ownTeam);
|
||||||
|
|
@ -190,6 +196,11 @@ namespace Luneraria.UI
|
||||||
tip.Stat("tower", "Gegner " + LuneHudModel.BuildingTitle(Data.Towers[index].Id), TowerValue(Data.Towers[index]));
|
tip.Stat("tower", "Gegner " + LuneHudModel.BuildingTitle(Data.Towers[index].Id), TowerValue(Data.Towers[index]));
|
||||||
if (Data.HasCounts)
|
if (Data.HasCounts)
|
||||||
tip.Stat("melee", "Einheiten", Data.OwnCount + " eigene · " + Data.EnemyCount + " gegnerische");
|
tip.Stat("melee", "Einheiten", Data.OwnCount + " eigene · " + Data.EnemyCount + " gegnerische");
|
||||||
|
// S1 barracks (Dota 2): a fallen inner tower reinforces the attackers' waves on this lane for good.
|
||||||
|
if (Data.OwnBarracks)
|
||||||
|
tip.Stat("melee", "Kaserne", "gegnerischer innerer Turm gefallen: eure Wellen +25 % und ein Monster mehr");
|
||||||
|
if (Data.EnemyBarracks)
|
||||||
|
tip.Stat("warning", "Kaserne", "euer innerer Turm ist gefallen: gegnerische Wellen +25 % und ein Monster mehr");
|
||||||
var notes = new List<string>();
|
var notes = new List<string>();
|
||||||
foreach (Announcement danger in Data.Dangers)
|
foreach (Announcement danger in Data.Dangers)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,22 @@ namespace Luneraria.UI
|
||||||
static int _muted = -1;
|
static int _muted = -1;
|
||||||
static int _fullscreen = -1;
|
static int _fullscreen = -1;
|
||||||
static int _ambientOcclusion = -1;
|
static int _ambientOcclusion = -1;
|
||||||
|
static int _extendedRules = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// S1: start matches with the extended rules (champion income, barracks, buyback,
|
||||||
|
/// Mondsegen; bots may pick Sylvara); on by default. Off = the reference rules of the port.
|
||||||
|
/// </summary>
|
||||||
|
public static bool ExtendedRules
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_extendedRules < 0)
|
||||||
|
_extendedRules = PlayerPrefs.GetInt("lune.extended_rules", 1);
|
||||||
|
return _extendedRules == 1;
|
||||||
|
}
|
||||||
|
set { _extendedRules = value ? 1 : 0; Save("lune.extended_rules", _extendedRules); }
|
||||||
|
}
|
||||||
|
|
||||||
public static float UiScale
|
public static float UiScale
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,9 @@ namespace Luneraria.UI
|
||||||
public int Enemy { get { return 1 - Team; } }
|
public int Enemy { get { return 1 - Team; } }
|
||||||
public int OwnGold { get { return State.GoldOf(Slot); } }
|
public int OwnGold { get { return State.GoldOf(Slot); } }
|
||||||
/// <summary>Base income paid at the end of the current round's combat (rules: LuneBalance.RoundIncome).</summary>
|
/// <summary>Base income paid at the end of the current round's combat (rules: LuneBalance.RoundIncome).</summary>
|
||||||
public int Income { get { return State.Balance.RoundIncome(State.RoundNumber); } }
|
public int Income { get { return State.Balance.RoundIncome(State.RoundNumber) + IncomeBonus; } }
|
||||||
|
/// <summary>S1: the own permanent champion income (Legion TD 2), 0 under the reference rules.</summary>
|
||||||
|
public int IncomeBonus { get { return State.Extended ? Me.IncomeBonus : 0; } }
|
||||||
/// <summary>After Ready the plan is binding (the host refuses later commands).</summary>
|
/// <summary>After Ready the plan is binding (the host refuses later commands).</summary>
|
||||||
public bool Locked { get { return Planning && Me.Ready; } }
|
public bool Locked { get { return Planning && Me.Ready; } }
|
||||||
public List<RosterEntry> OwnRoster { get { return Planning ? Me.Draft.Roster : Me.Roster; } }
|
public List<RosterEntry> OwnRoster { get { return Planning ? Me.Draft.Roster : Me.Roster; } }
|
||||||
|
|
@ -301,6 +303,9 @@ namespace Luneraria.UI
|
||||||
public bool HasCounts;
|
public bool HasCounts;
|
||||||
public int OwnCount;
|
public int OwnCount;
|
||||||
public int EnemyCount;
|
public int EnemyCount;
|
||||||
|
/// <summary>S1 barracks: own waves reinforced here (enemy inner tower fell) / enemy waves reinforced.</summary>
|
||||||
|
public bool OwnBarracks;
|
||||||
|
public bool EnemyBarracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LaneInfo Lane(string lane)
|
public LaneInfo Lane(string lane)
|
||||||
|
|
@ -314,6 +319,7 @@ namespace Luneraria.UI
|
||||||
Lane = lane, Title = LuneMap.LaneNames[lane].ToUpperInvariant(), OwnPlayer = PlayerName(ownSeat), EnemyPlayer = PlayerName(enemySeat), OwnIsMe = ownSeat == Slot,
|
Lane = lane, Title = LuneMap.LaneNames[lane].ToUpperInvariant(), OwnPlayer = PlayerName(ownSeat), EnemyPlayer = PlayerName(enemySeat), OwnIsMe = ownSeat == Slot,
|
||||||
Towers = new[] { Building(own + "_" + lane + "_inner"), Building(own + "_" + lane + "_outer"), Building(enemy + "_" + lane + "_outer"), Building(enemy + "_" + lane + "_inner") },
|
Towers = new[] { Building(own + "_" + lane + "_inner"), Building(own + "_" + lane + "_outer"), Building(enemy + "_" + lane + "_outer"), Building(enemy + "_" + lane + "_inner") },
|
||||||
Threat = LuneLaneThreat.Evaluate(State, Team, lane), OwnHero = HeroOf(ownSeat), EnemyHero = HeroOf(enemySeat),
|
Threat = LuneLaneThreat.Evaluate(State, Team, lane), OwnHero = HeroOf(ownSeat), EnemyHero = HeroOf(enemySeat),
|
||||||
|
OwnBarracks = State.Extended && State.BarracksBroken(Team, lane), EnemyBarracks = State.Extended && State.BarracksBroken(Enemy, lane),
|
||||||
};
|
};
|
||||||
foreach (Announcement entry in State.Announcements())
|
foreach (Announcement entry in State.Announcements())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ namespace Luneraria.View
|
||||||
public int SpeedIndex;
|
public int SpeedIndex;
|
||||||
public bool Paused;
|
public bool Paused;
|
||||||
public bool RecordTrace = true;
|
public bool RecordTrace = true;
|
||||||
|
/// <summary>S1: extended rules for matches the driver starts itself (tests, benchmark); the start screen decides for the game.</summary>
|
||||||
|
public bool Extended;
|
||||||
|
|
||||||
public LuneMatchRunner Runner { get; private set; }
|
public LuneMatchRunner Runner { get; private set; }
|
||||||
/// <summary>Main-thread cost of the last frame's simulation (milliseconds).</summary>
|
/// <summary>Main-thread cost of the last frame's simulation (milliseconds).</summary>
|
||||||
|
|
@ -51,7 +53,7 @@ namespace Luneraria.View
|
||||||
|
|
||||||
public void Restart()
|
public void Restart()
|
||||||
{
|
{
|
||||||
var options = new MatchOptions { Seed = Seed, HumanSlot = HumanSlot, Bunds = Bunds != null ? new List<string>(Bunds) : new List<string>() };
|
var options = new MatchOptions { Seed = Seed, HumanSlot = HumanSlot, Bunds = Bunds != null ? new List<string>(Bunds) : new List<string>(), Extended = Extended };
|
||||||
Runner = new LuneMatchRunner(options, StartRound, ExtraGold, Profile);
|
Runner = new LuneMatchRunner(options, StartRound, ExtraGold, Profile);
|
||||||
if (RecordTrace)
|
if (RecordTrace)
|
||||||
Runner.Trace = new LuneTrace();
|
Runner.Trace = new LuneTrace();
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,18 @@ Legende:
|
||||||
| Ping-Rad, Teamplanung | 🟢 | Absprachen wie „Grotte diese Runde?“. |
|
| Ping-Rad, Teamplanung | 🟢 | Absprachen wie „Grotte diese Runde?“. |
|
||||||
| Replays, Zuschauer | 🟢 | Die Simulation ist deterministisch; ein Replay sind Seed plus Befehle. |
|
| Replays, Zuschauer | 🟢 | Die Simulation ist deterministisch; ein Replay sind Seed plus Befehle. |
|
||||||
|
|
||||||
|
## Umsetzungsstand
|
||||||
|
|
||||||
|
| Idee | Stand |
|
||||||
|
| --- | --- |
|
||||||
|
| Ultimate-Bedingungen | umgesetzt in H1 (Sylvara) |
|
||||||
|
| Skins | umgesetzt in H1 (Blutmond) |
|
||||||
|
| Einkommen über Champions | umgesetzt in S1 (Söldner-Einkommen) |
|
||||||
|
| Kasernen | umgesetzt in S1 |
|
||||||
|
| Respawn und Rückkauf | umgesetzt in S1, abgeschwächt (Erschöpfung statt Aussetzen) |
|
||||||
|
| Drachen-Analogon | umgesetzt in S1 als Mondsegen über die Grotte |
|
||||||
|
| Schadens- und Rüstungstypen | bestand bereits (3 × 3); Farben im HUD seit G3 |
|
||||||
|
|
||||||
## Empfohlene Reihenfolge
|
## Empfohlene Reihenfolge
|
||||||
|
|
||||||
1. Ultimate- und Item-Bedingungen
|
1. Ultimate- und Item-Bedingungen
|
||||||
|
|
|
||||||
100
docs/luneraria/S1-STRATEGIE.md
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
# S1 · Strategie-Regeln nach Legion TD 2, Dota 2 und League of Legends
|
||||||
|
|
||||||
|
Stand: 11. Oktober 2026 · Branch `luneraria/s1-strategie`. Der Branch beginnt mit H1 (`luneraria/h1-sylvara`), in den `luneraria/u5.5` zusammengeführt wurde (Merge `2e05cbe`).
|
||||||
|
Grundlage: [IDEEN.md](IDEEN.md). Auftrag: „weitermachen, an Legion TD 2, Dota 2 und League of Legends orientieren“.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Kurzfassung
|
||||||
|
|
||||||
|
Vier Mechaniken, je eine typische Idee der Vorbilder, übertragen auf einen Autobattler ohne direkte Steuerung:
|
||||||
|
|
||||||
|
| Vorbild | Mechanik | Strategische Frage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Legion TD 2 (Söldner → Einkommen) | **Söldner-Einkommen:** Jeder gebuchte Angriffschampion erhöht das eigene Rundeneinkommen dauerhaft um Preis ÷ 12 (Rostschieber/Dunstsänger +12, Keilbrecher +15) | Jetzt Druck und Wirtschaft aufbauen oder Gold für die eigene Verteidigung behalten? |
|
||||||
|
| Dota 2 (Kasernen, Megacreeps) | **Kasernen:** Fällt der innere Turm einer Lane, kommen die Monsterwellen des Angreifers dort dauerhaft mit +25 % Leben und Schaden und einem Monster mehr | Welche Lane brechen wir, welche müssen wir halten? |
|
||||||
|
| Dota 2 (Rückkauf) | **Erschöpfung und Rückkauf:** Ein gefallener Held startet den nächsten Kampf mit 60 % Leben, außer er wird für 60 + 20 × Stufe Gold zurückgekauft | Gold in den Helden oder in die Armee? |
|
||||||
|
| League of Legends (Drachenseelen) | **Mondsegen:** Jeder Grottensieg gibt dem Team dauerhaft +4 % Leben und Schaden für alle Truppen und Helden, bis 4 Stapel. Die „Mondseele“ (4/4) lässt Helden zusätzlich 1 % Leben pro Sekunde regenerieren | Lohnt die Grotte jetzt, auch wenn die Lane dafür dünner wird? |
|
||||||
|
|
||||||
|
Außerdem nehmen die Bots mit erweiterten Regeln das Profil `assassine` in ihre Auswahl auf und wählen damit auch Sylvara. Das war die offene Frage aus H1.
|
||||||
|
|
||||||
|
## Regelschalter und Parität
|
||||||
|
|
||||||
|
- `MatchOptions.Extended` schaltet alles ein. Der Wert wird über das Netz an die Gäste übertragen.
|
||||||
|
- **Standard im Spiel: erweitert.** Der Startbildschirm hat den Schalter **REGELN** („ERWEITERT · Legion TD 2, Dota 2, LoL“ oder „REFERENZ“). Die Wahl wird in den Einstellungen gespeichert (`lune.extended_rules`).
|
||||||
|
- **Referenzregeln:** Ohne den Schalter gilt alles wie bisher. Das betrifft Golden-Replays, Fingerabdrücke, die HUD-Prüfung A–I und den Benchmark. Die Suite `s1` prüft ausdrücklich, dass keine Mechanik ohne Schalter wirkt.
|
||||||
|
- **Neue Zustände** werden über das Netz synchronisiert: `Player.IncomeBonus`, `HeroState.Exhausted`, `BossState.Blessing`. Neuer Befehl: `buyback`.
|
||||||
|
- **Zahlen:** Alle Werte stehen in `LuneBalance` (Abschnitt S1).
|
||||||
|
|
||||||
|
## Warum der Rückkauf abgeschwächt ist
|
||||||
|
|
||||||
|
In Dota 2 kostet ein Tod Zeit. In den Bot-Partien fallen Helden aber fast in jeder Runde (Ringborke nach etwa 19 s, Sylvara nach 13–20 s). „Eine Runde aussetzen“ würde jeden Helden jede zweite Runde stilllegen. Deshalb startet ein gefallener Held erschöpft mit 60 % Leben, und der Rückkauf stellt ihn voll her. So bleibt Dotas Abwägung „Gold gegen Stärke im nächsten Kampf“ erhalten, ohne dass Helden ganze Runden fehlen.
|
||||||
|
|
||||||
|
## Oberfläche
|
||||||
|
|
||||||
|
| Stelle | Anzeige |
|
||||||
|
| --- | --- |
|
||||||
|
| Gold unten links | „+109 · davon 15 Söldner“; der Tooltip nennt das Söldner-Einkommen |
|
||||||
|
| Angriffschampion-Tooltip | „Söldner-Einkommen: +15 Gold in jeder folgenden Runde, dauerhaft.“ |
|
||||||
|
| Lane-Anzeige | gekreuzte Klingen: in Teamfarbe, wenn die eigenen Wellen verstärkt sind, rot bei verstärkten Gegnerwellen; der Lane-Tooltip erklärt die Kaserne |
|
||||||
|
| Heldenkarte | „1.450 Leben · ERSCHÖPFT“ |
|
||||||
|
| Heldenfenster | „ERSCHÖPFT: gefallen, startet mit 60 % Leben.“ und **Rückkauf · 80 G** |
|
||||||
|
| Teamkopf | „DEIN TEAM · BLAU · SEGEN 2/4“ |
|
||||||
|
| Startbildschirm | Schalter REGELN mit Tooltip, der alle vier Mechaniken und ihre Vorbilder nennt |
|
||||||
|
|
||||||
|
## Balance (Bot-Partien)
|
||||||
|
|
||||||
|
Werkzeug: `dotnet run -c Release --project tools/dotnet/Luneraria.Balance s1 60`. Gespielt werden 60 Seeds mit der normalen Profil-Rotation, Rohdaten in `docs/port/logs/s1/balance-s1.txt`.
|
||||||
|
|
||||||
|
| | Referenz | Erweitert |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Siege Blau / Rot | 30 / 30 | 29 / 31 |
|
||||||
|
| Ø Runden (min–max) | 10,7 (9–12) | 12,8 (11–15) |
|
||||||
|
| Wer den ersten Turm nimmt, gewinnt | 40 von 60 | 43 von 60 |
|
||||||
|
| Söldner-Einkommen je Platz am Ende | – | 14,1 |
|
||||||
|
| Kasernen-verstärkte Lane-Wellen | – | 103 |
|
||||||
|
| Erschöpfte Helden / Rückkäufe | – | 832 / 520 |
|
||||||
|
| Mondsegen-Stapel (gesamt) | – | 8 |
|
||||||
|
| Sylvara (Bot-Plätze / Siege) | – | 48 / 24 (50 %) |
|
||||||
|
|
||||||
|
Bewertung:
|
||||||
|
- **Beide Seiten bleiben ausgeglichen.** Die Partien dauern etwa zwei Runden länger, es gibt also mehr Planungsentscheidungen.
|
||||||
|
- **Vorsprünge schaukeln sich nur leicht stärker auf** (40 → 43 von 60). Die Kasernen erzeugen Druck, entscheiden die Partie aber nicht allein.
|
||||||
|
- **Rückkauf ist eine echte Goldsenke:** Bots kaufen in etwa 60 % der Fälle zurück.
|
||||||
|
- **Mondsegen ist selten,** weil nur das Bot-Profil `bossjagd` die Grotte angreift. Menschen können ihn gezielt suchen. Eine spätere Bot-Phase sollte die Grotte häufiger angehen.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
| Prüfung | Ergebnis |
|
||||||
|
| --- | --- |
|
||||||
|
| Kern (.NET 8) | 531/531, darunter **s1 31** (Referenz unberührt, Söldner-Einkommen, Kasernen, Erschöpfung und Rückkauf samt Rückgängig, Mondsegen bis zur Seele, Bot-Rotation mit Sylvara, Determinismus) |
|
||||||
|
| Golden und Fingerabdrücke | 0 Abweichungen; `DCE95C96556F3A1A` bei 30/60/120 fps und unbegrenzt unverändert (die Referenzregeln bleiben Standard der Testfahrer) |
|
||||||
|
| Unity EditMode | 31/31 |
|
||||||
|
| Unity PlayMode | 16/16 (neu `S1RulesViewsTests.S1Hud`), Verify A–I 718/0 |
|
||||||
|
| Player-Selbsttest | macOS 30/30, Startbildschirm mit Regelschalter (`screenshots/s1/spiel/00-startbildschirm-regeln.jpg`) |
|
||||||
|
|
||||||
|
## Leistung
|
||||||
|
|
||||||
|
S1 ändert nur Regeln und HUD-Texte. Benchmark im S1-Player, Seed 9 ab Welle 2, sechs Sylvaras auf dem Feld: Karte 150,3 · 148,5 · 148,5 fps, Mid nah 170,8 · 170,1 · 171,7 fps. HUD-Skripte 0,053 bzw. 0,039 ms pro Bild, also im Rahmen von U3 bis H1. Der Lauf fand unter geringerer Systemlast statt als H1 (136/159 fps); die absoluten Werte sind deshalb nicht direkt vergleichbar. Rohdaten liegen in `docs/port/logs/s1/`.
|
||||||
|
|
||||||
|
## Bilder
|
||||||
|
|
||||||
|
`docs/port/screenshots/s1/spiel/`:
|
||||||
|
- 00: Startbildschirm mit Regelschalter
|
||||||
|
- 01: Planung mit Söldner-Einkommen, Kaserne und Segen
|
||||||
|
- 02: Kasernen-Tooltip
|
||||||
|
- 03: Rückkauf im Heldenfenster
|
||||||
|
- 04: verstärkte Welle auf Mid
|
||||||
|
|
||||||
|
Unter `ablauf/` liegen die Momente des Vertical Function Test.
|
||||||
|
|
||||||
|
## Offene Punkte
|
||||||
|
|
||||||
|
- **Mondsegen-Häufigkeit:** Die Bot-Profile sollten die Grotte öfter angreifen. Das betrifft nur die erweiterten Regeln.
|
||||||
|
- **Weitere Ideen aus IDEEN.md** für eine S2:
|
||||||
|
- Fog of War mit Spähern (Dota-Wards)
|
||||||
|
- Schadens- und Rüstungsfarben stärker im HUD (das Typensystem 3 × 3 besteht schon, nach Legion TD)
|
||||||
|
- Glyphe
|
||||||
|
- Neutrallager und Schreine (LoL-Buffs)
|
||||||
|
- Item-Rezepte
|
||||||
|
- **Nächster Held** nach dem H1-Muster.
|
||||||
|
|
@ -276,6 +276,18 @@ Die Glutpakt-Arbeit entstand als Kette aufeinander aufbauender Zweige (`lunerari
|
||||||
| Bekannte Unterschiede | gewollte Erweiterung über die Referenz hinaus ([../luneraria/H1-SYLVARA.md](../luneraria/H1-SYLVARA.md)) |
|
| Bekannte Unterschiede | gewollte Erweiterung über die Referenz hinaus ([../luneraria/H1-SYLVARA.md](../luneraria/H1-SYLVARA.md)) |
|
||||||
| Commits | `luneraria/h1-sylvara` |
|
| Commits | `luneraria/h1-sylvara` |
|
||||||
|
|
||||||
|
## S1 · Strategie-Regeln (Legion TD 2, Dota 2, League of Legends)
|
||||||
|
|
||||||
|
| Punkt | Ergebnis |
|
||||||
|
| --- | --- |
|
||||||
|
| Tests | Kern .NET 531/531 (neu `s1` 31), EditMode 31/31, PlayMode 16/16 (neu `S1Hud`), Verify A–I 718/0, Player-Selbsttest 30/30 |
|
||||||
|
| Parität | Referenzregeln unverändert Standard der Tests: Golden 0 Abweichungen, Fingerabdruck `DCE95C96556F3A1A` gleich; `s1` prüft, dass ohne `Extended` nichts wirkt |
|
||||||
|
| Inhalt | Söldner-Einkommen, Kasernen, Erschöpfung und Rückkauf, Mondsegen; Bots wählen Sylvara mit erweiterten Regeln; Regelschalter im Startbildschirm (Standard: erweitert) |
|
||||||
|
| Balance | 60 Bot-Partien je Regelwerk: Blau/Rot 30/30 zu 29/31, Ø Runden 10,7 zu 12,8, Sieg nach erstem Turm 40 zu 43 (`logs/s1/balance-s1.txt`) |
|
||||||
|
| Leistung | Karte 148,5–150,3 fps, Mid nah 170,1–171,7 fps, HUD 0,04–0,05 ms (`logs/s1/`) |
|
||||||
|
| Bild | `screenshots/s1/spiel/` |
|
||||||
|
| Commits | `luneraria/s1-strategie` (Basis: Merge H1 + U5.5 `2e05cbe`) |
|
||||||
|
|
||||||
## Ausblick
|
## Ausblick
|
||||||
|
|
||||||
Der Pilot entscheidet über eine Serie weiterer Meshy-Figuren (Kosten je Figur, Lizenz, Stilgleichheit). Offen bleiben außerdem Künstler-Sculpts für Anatomie/Gesicht/Haar, LOD und Material-Atlanten sowie die Build-Module für Windows und Linux.
|
Der Pilot entscheidet über eine Serie weiterer Meshy-Figuren (Kosten je Figur, Lizenz, Stilgleichheit). Offen bleiben außerdem Künstler-Sculpts für Anatomie/Gesicht/Haar, LOD und Material-Atlanten sowie die Build-Module für Windows und Linux.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Port-Status · Champions of Luneraria in Unity
|
# Port-Status · Champions of Luneraria in Unity
|
||||||
|
|
||||||
Stand: 11. Oktober 2026 (H1 Sylvara und U5.5 zusammengeführt; S1 in Arbeit).
|
Stand: 11. Oktober 2026 (S1, Strategie-Regeln).
|
||||||
|
|
||||||
Referenz ist der Godot-Stand `luneraria/g3` (`1b540f2`) in `../saumwacht-godot`; er wird nicht mehr verändert. Engine: Unity 6.3 LTS (6000.3.20f1), Renderer URP. HDRP ist ausgeschlossen, bis ein Vergleich mit einer echten Szene etwas anderes nahelegt.
|
Referenz ist der Godot-Stand `luneraria/g3` (`1b540f2`) in `../saumwacht-godot`; er wird nicht mehr verändert. Engine: Unity 6.3 LTS (6000.3.20f1), Renderer URP. HDRP ist ausgeschlossen, bis ein Vergleich mit einer echten Szene etwas anderes nahelegt.
|
||||||
|
|
||||||
|
|
@ -15,6 +15,7 @@ Zielplattformen sind Fedora 44 und Windows 11; macOS ist die Entwicklungsplattfo
|
||||||
| U4 · Netzwerk | **fertig** (`luneraria/u4` = `769e622`): LuneSession (Protokoll 4), TCP-Transport, Startbildschirm Host/Join, 43+12 Prüfungen, Mindestumfang M4. Nachtrag (`luneraria/u4-nachtrag`, in U5.3 zusammengeführt): Startbildschirm in der Szene, „Menü“ führt dorthin zurück | [U4-NETWORK.md](U4-NETWORK.md) |
|
| U4 · Netzwerk | **fertig** (`luneraria/u4` = `769e622`): LuneSession (Protokoll 4), TCP-Transport, Startbildschirm Host/Join, 43+12 Prüfungen, Mindestumfang M4. Nachtrag (`luneraria/u4-nachtrag`, in U5.3 zusammengeführt): Startbildschirm in der Szene, „Menü“ führt dorthin zurück | [U4-NETWORK.md](U4-NETWORK.md) |
|
||||||
| U5 · Visuelle Neuausrichtung (Vertical Slice Mid-Lane) | **fertig** (`luneraria/u5` = `2c1686b`, Stufen `luneraria/u5.2`): 6 PBR-Sätze, Surface-Shader mit Albedo/Normal/ORM, Stufen Lane/Rand, Hit- und Turm-VFX, 133/180 fps. **U5.3** (`luneraria/u5.3` = `07fa457`): Gebäude, Boss und Ringborkes Rinde auf PBR (11 Sätze); Bildfehler aus U5 behoben (doppelte Einfärbung, Datenkarten als sRGB, Streifen, bleichender Treffer-Flash, Nebel), Helligkeit und Grün wieder auf U3-Niveau **U5.4** (`luneraria/u5.4` = `4883d95`): Surface-Materialien für alle 146 Figurenmaterialien (Metalle metallisch, Randlicht, Struktur in den Porträts), SSAO (abschaltbar), Leistung −13 % Karte / −16 % Mid **U5.5** (`luneraria/u5.5` = `dd78c8d`): **Pilot** (`c386206`): Meshy-Figur für den Kohlenbeißer `g01` (Pipeline `tools/meshy/`, UV-Material mit Lava-Glut, Schatten ohne Normalen-Versatz), GPU-Batched-Skinning, Figuren-Stress −21 % Mid / −13 % Karte gegenüber Phase-4; **Lane-Fassung** (`f0d1e93`): 5.000-Dreiecke-LOD, Abstand −10 % Mid / −2 % Karte; **Schlackenklaue** `g02` (`714d0c6`) als zweite Meshy-Figur, −11 % Mid / −1 % Karte; **Schlackenwacht** `g03` (`4e2b6bc`), −8 % Mid / 0 % Karte; **Funkenlanze** `g05` mit Lanze als eigenem Modell (`eddf49d`), +15 % gegenüber der Phase-18-Figur; **Glasballiste** `g06` als Maschine mit übernommenem Skelett (`aa77d0e`); **Aschenwerfer** `g07` (`91ea297`), −11 % Mid / +2 % Karte; **Kraterrufer** `g08` (`eb8a537`), −9 % Mid / +10 % Karte; **Herdhüter** `g09` (`024937c`), −4 % Mid / +6 % Karte; **Sonnenesse** `g10` (`02c8839`), +11 % Mid / +15 % Karte; **Basaltbastion** `g04` (`9e253f4`): alle zehn Glutpakt-Truppen sind Meshy-Figuren | [U5-VISUAL.md](U5-VISUAL.md) |
|
| U5 · Visuelle Neuausrichtung (Vertical Slice Mid-Lane) | **fertig** (`luneraria/u5` = `2c1686b`, Stufen `luneraria/u5.2`): 6 PBR-Sätze, Surface-Shader mit Albedo/Normal/ORM, Stufen Lane/Rand, Hit- und Turm-VFX, 133/180 fps. **U5.3** (`luneraria/u5.3` = `07fa457`): Gebäude, Boss und Ringborkes Rinde auf PBR (11 Sätze); Bildfehler aus U5 behoben (doppelte Einfärbung, Datenkarten als sRGB, Streifen, bleichender Treffer-Flash, Nebel), Helligkeit und Grün wieder auf U3-Niveau **U5.4** (`luneraria/u5.4` = `4883d95`): Surface-Materialien für alle 146 Figurenmaterialien (Metalle metallisch, Randlicht, Struktur in den Porträts), SSAO (abschaltbar), Leistung −13 % Karte / −16 % Mid **U5.5** (`luneraria/u5.5` = `dd78c8d`): **Pilot** (`c386206`): Meshy-Figur für den Kohlenbeißer `g01` (Pipeline `tools/meshy/`, UV-Material mit Lava-Glut, Schatten ohne Normalen-Versatz), GPU-Batched-Skinning, Figuren-Stress −21 % Mid / −13 % Karte gegenüber Phase-4; **Lane-Fassung** (`f0d1e93`): 5.000-Dreiecke-LOD, Abstand −10 % Mid / −2 % Karte; **Schlackenklaue** `g02` (`714d0c6`) als zweite Meshy-Figur, −11 % Mid / −1 % Karte; **Schlackenwacht** `g03` (`4e2b6bc`), −8 % Mid / 0 % Karte; **Funkenlanze** `g05` mit Lanze als eigenem Modell (`eddf49d`), +15 % gegenüber der Phase-18-Figur; **Glasballiste** `g06` als Maschine mit übernommenem Skelett (`aa77d0e`); **Aschenwerfer** `g07` (`91ea297`), −11 % Mid / +2 % Karte; **Kraterrufer** `g08` (`eb8a537`), −9 % Mid / +10 % Karte; **Herdhüter** `g09` (`024937c`), −4 % Mid / +6 % Karte; **Sonnenesse** `g10` (`02c8839`), +11 % Mid / +15 % Karte; **Basaltbastion** `g04` (`9e253f4`): alle zehn Glutpakt-Truppen sind Meshy-Figuren | [U5-VISUAL.md](U5-VISUAL.md) |
|
||||||
| H1 · Sylvara | **fertig** (`luneraria/h1-sylvara`): erste spielbare Heldin nach dem Port mit Ultimate-Auslöser in der Planung und Skin-System (Blutmond); Referenzpartien unverändert | [../luneraria/H1-SYLVARA.md](../luneraria/H1-SYLVARA.md) |
|
| H1 · Sylvara | **fertig** (`luneraria/h1-sylvara`): erste spielbare Heldin nach dem Port mit Ultimate-Auslöser in der Planung und Skin-System (Blutmond); Referenzpartien unverändert | [../luneraria/H1-SYLVARA.md](../luneraria/H1-SYLVARA.md) |
|
||||||
|
| S1 · Strategie-Regeln | **fertig** (`luneraria/s1-strategie`): Söldner-Einkommen (Legion TD 2), Kasernen und Rückkauf (Dota 2), Mondsegen (League of Legends) hinter dem Regelschalter „Erweitert“ (Standard im Spiel); Referenzregeln für Tests unverändert | [../luneraria/S1-STRATEGIE.md](../luneraria/S1-STRATEGIE.md) |
|
||||||
|
|
||||||
QA je Meilenstein: [PORT-QA.md](PORT-QA.md).
|
QA je Meilenstein: [PORT-QA.md](PORT-QA.md).
|
||||||
|
|
||||||
|
|
|
||||||
3
docs/port/logs/s1/balance-s1.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
rules; matches; blue wins; red wins; draws; open; mean rounds; min; max; first tower wins; champion income/seat; barracks waves; exhausted heroes; buybacks; blessings; sylvara seats; sylvara wins
|
||||||
|
referenz; 60; 30; 30; 0; 0; 10.7; 9; 12; 40/60; 0.0; 0; 0; 0; 0; 0; 0
|
||||||
|
erweitert; 60; 29; 31; 0; 0; 12.8; 11; 15; 43/60; 14.1; 103; 832; 520; 8; 48; 24
|
||||||
16
docs/port/logs/s1/core-tests-dotnet.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"suites": [
|
||||||
|
{"suite": "content", "checks": 76, "failures": 0, "seconds": 0.02, "failed": [], "notes": ["map geometry: largest deviation lanes 0.00E+000 m, buildings 0.00E+000 m"]},
|
||||||
|
{"suite": "core", "checks": 91, "failures": 0, "seconds": 1.54, "failed": [], "notes": ["full match seed 2 -> Rot after 9 rounds"]},
|
||||||
|
{"suite": "m2", "checks": 73, "failures": 0, "seconds": 0.38, "failed": [], "notes": []},
|
||||||
|
{"suite": "golden", "checks": 157, "failures": 0, "seconds": 1.49, "failed": [], "notes": ["golden values compared: 51221", "largest deviation angle: 0.00E+000 over 1140 values (tolerance 1E-004)", "largest deviation effect: 0.00E+000 over 264 values (tolerance 1E-006)", "largest deviation hp: 0.00E+000 over 5679 values (tolerance 1E-002)", "largest deviation other: 0.00E+000 over 34 values (tolerance 1E-006)", "largest deviation position: 0.00E+000 over 3420 values (tolerance 1E-003)", "largest deviation time: 0.00E+000 over 780 values (tolerance 1E-006)", "domain ancients: 246 values, 0 differences, largest numeric deviation 0.00E+000", "domain announcements: 44 values, 0 differences, largest numeric deviation 0.00E+000", "domain boss: 1559 values, 0 differences, largest numeric deviation 0.00E+000", "domain combat: 676 values, 0 differences, largest numeric deviation 0.00E+000", "domain commands: 410 values, 0 differences, largest numeric deviation 0.00E+000", "domain events: 6937 values, 0 differences, largest numeric deviation 0.00E+000", "domain facing: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain gold: 3584 values, 0 differences, largest numeric deviation 0.00E+000", "domain heroes: 1512 values, 0 differences, largest numeric deviation 0.00E+000", "domain hp: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain other: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain positions: 3420 values, 0 differences, largest numeric deviation 0.00E+000", "domain purchases: 9311 values, 0 differences, largest numeric deviation 0.00E+000", "domain rounds: 119 values, 0 differences, largest numeric deviation 0.00E+000", "domain targets: 1140 values, 0 differences, largest numeric deviation 0.00E+000", "domain towers: 1994 values, 0 differences, largest numeric deviation 0.00E+000", "domain units: 14164 values, 0 differences, largest numeric deviation 0.00E+000", "domain waves: 731 values, 0 differences, largest numeric deviation 0.00E+000", "domain winner: 695 values, 0 differences, largest numeric deviation 0.00E+000"]},
|
||||||
|
{"suite": "determinism", "checks": 6, "failures": 0, "seconds": 1.45, "failed": [], "notes": []},
|
||||||
|
{"suite": "net", "checks": 43, "failures": 0, "seconds": 0.01, "failed": [], "notes": []},
|
||||||
|
{"suite": "net_rounds", "checks": 12, "failures": 0, "seconds": 0.07, "failed": [], "notes": []},
|
||||||
|
{"suite": "h1", "checks": 42, "failures": 0, "seconds": 1.00, "failed": [], "notes": ["sylvara-bot match seed 12: 13/1"]},
|
||||||
|
{"suite": "s1", "checks": 31, "failures": 0, "seconds": 0.60, "failed": [], "notes": ["extended seed 17: 14/0, income bonus 24,24,24,24,24,24, blessing 0/0"]}
|
||||||
|
],
|
||||||
|
"runtime": ".NET 8.0.27",
|
||||||
|
"label": ".NET 8.0.27, Release",
|
||||||
|
"os": "Darwin 27.0.0 Darwin Kernel Version 27.0.0: Tue Aug 11 21:02:59 PDT 2026; root:xnu-13432.1.9~1/RELEASE_ARM64_T8142 Arm64"
|
||||||
|
}
|
||||||
346
docs/port/logs/s1/editmode-results.xml
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="31" result="Passed" total="31" passed="31" failed="0" inconclusive="0" skipped="0" asserts="0" engine-version="3.5.0.0" clr-version="4.0.30319.42000" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:07:12Z" duration="73.0438993">
|
||||||
|
<test-suite type="TestSuite" id="1000" name="luneraria-unity" fullname="luneraria-unity" runstate="Runnable" testcasecount="31" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:07:12Z" duration="73.043899" total="31" passed="31" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="Assembly" id="1014" name="Luneraria.Core.Tests.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.Core.Tests.dll" runstate="Runnable" testcasecount="11" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:49Z" duration="50.146163" total="11" passed="11" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99255" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1015" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="11" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:49Z" duration="50.145967" total="11" passed="11" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1016" name="Core" fullname="Luneraria.Core" runstate="Runnable" testcasecount="11" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:49Z" duration="50.145760" total="11" passed="11" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1017" name="Tests" fullname="Luneraria.Core.Tests" runstate="Runnable" testcasecount="11" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:49Z" duration="50.145648" total="11" passed="11" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1001" name="CoreIntegrationTests" fullname="Luneraria.Core.Tests.CoreIntegrationTests" classname="Luneraria.Core.Tests.CoreIntegrationTests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:05:59Z" duration="0.051994" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1003" name="CoreFloatArithmeticMatchesGodot" fullname="Luneraria.Core.Tests.CoreIntegrationTests.CoreFloatArithmeticMatchesGodot" methodname="CoreFloatArithmeticMatchesGodot" classname="Luneraria.Core.Tests.CoreIntegrationTests" runstate="Runnable" seed="312334347" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:05:59Z" duration="0.047026" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[float probe: length 4000/4000, length_squared 4000/4000, distance_to 4000/4000, distance_squared_to 4000/4000, dot 4000/4000, normalized 4000/4000, lerp 4000/4000, limit_length 4000/4000, multiply 4000/4000, divide 4000/4000, sub_normalized 4000/4000, atan2 4000/4000
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1002" name="CoreHasNoEngineReferences" fullname="Luneraria.Core.Tests.CoreIntegrationTests.CoreHasNoEngineReferences" methodname="CoreHasNoEngineReferences" classname="Luneraria.Core.Tests.CoreIntegrationTests" runstate="Runnable" seed="1249370703" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:05:59Z" duration="0.000317" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1004" name="CoreSuiteTests" fullname="Luneraria.Core.Tests.CoreSuiteTests" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" testcasecount="9" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:49Z" duration="50.093069" total="9" passed="9" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1005" name="Content" fullname="Luneraria.Core.Tests.CoreSuiteTests.Content" methodname="Content" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="959581978" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:05:59Z" duration="0.011829" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[content: map geometry: largest deviation lanes 0.00E+000 m, buildings 0.00E+000 m
|
||||||
|
LUNE_U1_PASS content: 76 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1006" name="CoreRules" fullname="Luneraria.Core.Tests.CoreSuiteTests.CoreRules" methodname="CoreRules" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="1659583322" result="Passed" start-time="2026-10-11 10:05:59Z" end-time="2026-10-11 10:06:04Z" duration="5.311404" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[core: full match seed 2 -> Rot after 9 rounds
|
||||||
|
LUNE_U1_PASS core: 91 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1009" name="Determinism" fullname="Luneraria.Core.Tests.CoreSuiteTests.Determinism" methodname="Determinism" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="41469914" result="Passed" start-time="2026-10-11 10:06:04Z" end-time="2026-10-11 10:06:18Z" duration="13.642625" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_U1_PASS determinism: 6 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1008" name="GoldenReplays" fullname="Luneraria.Core.Tests.CoreSuiteTests.GoldenReplays" methodname="GoldenReplays" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="1193946597" result="Passed" start-time="2026-10-11 10:06:18Z" end-time="2026-10-11 10:06:31Z" duration="13.378805" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[golden: golden values compared: 51221
|
||||||
|
golden: largest deviation angle: 0.00E+000 over 1140 values (tolerance 1E-004)
|
||||||
|
golden: largest deviation effect: 0.00E+000 over 264 values (tolerance 1E-006)
|
||||||
|
golden: largest deviation hp: 0.00E+000 over 5679 values (tolerance 1E-002)
|
||||||
|
golden: largest deviation other: 0.00E+000 over 34 values (tolerance 1E-006)
|
||||||
|
golden: largest deviation position: 0.00E+000 over 3420 values (tolerance 1E-003)
|
||||||
|
golden: largest deviation time: 0.00E+000 over 780 values (tolerance 1E-006)
|
||||||
|
golden: domain ancients: 246 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain announcements: 44 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain boss: 1559 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain combat: 676 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain commands: 410 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain events: 6937 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain facing: 1140 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain gold: 3584 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain heroes: 1512 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain hp: 3420 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain other: 119 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain positions: 3420 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain purchases: 9311 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain rounds: 119 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain targets: 1140 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain towers: 1994 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain units: 14164 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain waves: 731 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
golden: domain winner: 695 values, 0 differences, largest numeric deviation 0.00E+000
|
||||||
|
LUNE_U1_PASS golden: 157 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1012" name="H1Heroes" fullname="Luneraria.Core.Tests.CoreSuiteTests.H1Heroes" methodname="H1Heroes" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="791088646" result="Passed" start-time="2026-10-11 10:06:31Z" end-time="2026-10-11 10:06:40Z" duration="9.117582" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[h1: sylvara-bot match seed 12: 13/1
|
||||||
|
LUNE_U1_PASS h1: 42 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1007" name="M2Rules" fullname="Luneraria.Core.Tests.CoreSuiteTests.M2Rules" methodname="M2Rules" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="1773736099" result="Passed" start-time="2026-10-11 10:06:40Z" end-time="2026-10-11 10:06:44Z" duration="3.356411" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_U1_PASS m2: 73 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1010" name="NetProtocol" fullname="Luneraria.Core.Tests.CoreSuiteTests.NetProtocol" methodname="NetProtocol" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="659779217" result="Passed" start-time="2026-10-11 10:06:44Z" end-time="2026-10-11 10:06:44Z" duration="0.065375" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_U1_PASS net: 43 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1011" name="NetRounds" fullname="Luneraria.Core.Tests.CoreSuiteTests.NetRounds" methodname="NetRounds" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="17740319" result="Passed" start-time="2026-10-11 10:06:44Z" end-time="2026-10-11 10:06:44Z" duration="0.563819" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_U1_PASS net_rounds: 12 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1013" name="S1Rules" fullname="Luneraria.Core.Tests.CoreSuiteTests.S1Rules" methodname="S1Rules" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="1242207057" result="Passed" start-time="2026-10-11 10:06:44Z" end-time="2026-10-11 10:06:49Z" duration="4.643125" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[s1: extended seed 17: 14/0, income bonus 24,24,24,24,24,24, blessing 0/0
|
||||||
|
LUNE_U1_PASS s1: 31 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="Assembly" id="1023" name="Luneraria.Net.Tests.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.Net.Tests.dll" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.009618" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99255" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1024" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.006645" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1025" name="Net" fullname="Luneraria.Net" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.006446" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1026" name="Tests" fullname="Luneraria.Net.Tests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.006354" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1018" name="LuneWireTests" fullname="Luneraria.Net.Tests.LuneWireTests" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.006202" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1019" name="CommandRoundTrips" fullname="Luneraria.Net.Tests.LuneWireTests.CommandRoundTrips" methodname="CommandRoundTrips" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="152124503" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.001447" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1022" name="LobbyAndOptionsRoundTrip" fullname="Luneraria.Net.Tests.LuneWireTests.LobbyAndOptionsRoundTrip" methodname="LobbyAndOptionsRoundTrip" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="1525343949" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000644" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1020" name="StateRoundTrips" fullname="Luneraria.Net.Tests.LuneWireTests.StateRoundTrips" methodname="StateRoundTrips" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="711433873" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.002263" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1021" name="UnitsAndFrameRoundTrip" fullname="Luneraria.Net.Tests.LuneWireTests.UnitsAndFrameRoundTrip" methodname="UnitsAndFrameRoundTrip" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="1175015924" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.001343" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="Assembly" id="1040" name="Luneraria.UI.Tests.EditMode.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.UI.Tests.EditMode.dll" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.193859" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99255" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1041" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.193660" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1042" name="UI" fullname="Luneraria.UI" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.193460" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1043" name="Tests" fullname="Luneraria.UI.Tests" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.193223" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1027" name="HudModelTests" fullname="Luneraria.UI.Tests.HudModelTests" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.192953" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1032" name="AnnouncedDangersRaiseTheThreat" fullname="Luneraria.UI.Tests.HudModelTests.AnnouncedDangersRaiseTheThreat" methodname="AnnouncedDangersRaiseTheThreat" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1043739073" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.003465" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1033" name="BattlefieldCountsTheLaneUnits" fullname="Luneraria.UI.Tests.HudModelTests.BattlefieldCountsTheLaneUnits" methodname="BattlefieldCountsTheLaneUnits" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="517940477" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.029582" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1030" name="BossLaneChoiceIsPrivateToTheChooser" fullname="Luneraria.UI.Tests.HudModelTests.BossLaneChoiceIsPrivateToTheChooser" methodname="BossLaneChoiceIsPrivateToTheChooser" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1167963491" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.002463" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1034" name="BossStatesFromPublicState" fullname="Luneraria.UI.Tests.HudModelTests.BossStatesFromPublicState" methodname="BossStatesFromPublicState" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1131672235" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000642" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1039" name="BotMatchStillStartsByItself" fullname="Luneraria.UI.Tests.HudModelTests.BotMatchStillStartsByItself" methodname="BotMatchStillStartsByItself" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1287812944" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.001099" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1028" name="EverySeatSeesTheSamePublicPicture" fullname="Luneraria.UI.Tests.HudModelTests.EverySeatSeesTheSamePublicPicture" methodname="EverySeatSeesTheSamePublicPicture" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1493506602" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.040516" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1038" name="HumanSeatPlanningWaitsForReady" fullname="Luneraria.UI.Tests.HudModelTests.HumanSeatPlanningWaitsForReady" methodname="HumanSeatPlanningWaitsForReady" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="470306608" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.055236" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1031" name="LaneThreatFollowsTowerHealth" fullname="Luneraria.UI.Tests.HudModelTests.LaneThreatFollowsTowerHealth" methodname="LaneThreatFollowsTowerHealth" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="536191432" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000864" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1035" name="NextWaveIsTheRulesWave" fullname="Luneraria.UI.Tests.HudModelTests.NextWaveIsTheRulesWave" methodname="NextWaveIsTheRulesWave" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1682600016" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.001262" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1037" name="NumbersAreFormattedAsInGodot" fullname="Luneraria.UI.Tests.HudModelTests.NumbersAreFormattedAsInGodot" methodname="NumbersAreFormattedAsInGodot" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="2145114089" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000453" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1029" name="OtherSeatsPlansStayPrivateUntilCombat" fullname="Luneraria.UI.Tests.HudModelTests.OtherSeatsPlansStayPrivateUntilCombat" methodname="OtherSeatsPlansStayPrivateUntilCombat" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="977222185" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.055487" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1036" name="ReadyLocksThePlan" fullname="Luneraria.UI.Tests.HudModelTests.ReadyLocksThePlan" methodname="ReadyLocksThePlan" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="44299803" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000759" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="Assembly" id="1049" name="Luneraria.View.Tests.EditMode.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.View.Tests.EditMode.dll" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:07:12Z" duration="22.691629" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99255" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1050" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:07:12Z" duration="22.691268" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1051" name="View" fullname="Luneraria.View" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:07:12Z" duration="22.691056" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1052" name="Tests" fullname="Luneraria.View.Tests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:07:12Z" duration="22.690837" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1044" name="FrameRateIndependenceTests" fullname="Luneraria.View.Tests.FrameRateIndependenceTests" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:07:12Z" duration="22.690590" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1048" name="ClockRunsWholeStepsOnly" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.ClockRunsWholeStepsOnly" methodname="ClockRunsWholeStepsOnly" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="1166163128" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:49Z" duration="0.000562" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1045" name="FullBotMatchSeed2IsTheSameAtEveryFrameRate" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.FullBotMatchSeed2IsTheSameAtEveryFrameRate" methodname="FullBotMatchSeed2IsTheSameAtEveryFrameRate" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="29110850" result="Passed" start-time="2026-10-11 10:06:49Z" end-time="2026-10-11 10:06:58Z" duration="8.863619" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_FPS seed 2 30 fps: 19315 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 60 fps: 38597 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 120 fps: 77169 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 unlimited (0.4-3.9 ms jitter): 569627 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 stutter (stalls up to 0.4 s): 26845 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1046" name="HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" methodname="HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="1705708230" result="Passed" start-time="2026-10-11 10:06:58Z" end-time="2026-10-11 10:07:03Z" duration="5.001773" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_FPS seed 9 30 fps: 6342 frames, 4195 steps, hash 7B8E43AEFC0D60C5 (core 7B8E43AEFC0D60C5)
|
||||||
|
LUNE_FPS seed 9 60 fps: 12678 frames, 4195 steps, hash 7B8E43AEFC0D60C5 (core 7B8E43AEFC0D60C5)
|
||||||
|
LUNE_FPS seed 9 120 fps: 25352 frames, 4195 steps, hash 7B8E43AEFC0D60C5 (core 7B8E43AEFC0D60C5)
|
||||||
|
LUNE_FPS seed 9 unlimited (0.4-3.9 ms jitter): 99090 frames, 4195 steps, hash 7B8E43AEFC0D60C5 (core 7B8E43AEFC0D60C5)
|
||||||
|
LUNE_FPS seed 9 stutter (stalls up to 0.4 s): 8796 frames, 4195 steps, hash 7B8E43AEFC0D60C5 (core 7B8E43AEFC0D60C5)
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1047" name="MatchSpeedDoesNotChangeTheMatch" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.MatchSpeedDoesNotChangeTheMatch" methodname="MatchSpeedDoesNotChangeTheMatch" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="230268251" result="Passed" start-time="2026-10-11 10:07:03Z" end-time="2026-10-11 10:07:12Z" duration="8.823781" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_FPS seed 2 30 fps: 2418 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 60 fps: 4831 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 120 fps: 9658 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 unlimited (0.4-3.9 ms jitter): 37713 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
LUNE_FPS seed 2 stutter (stalls up to 0.4 s): 3428 frames, 12690 steps, hash DCE95C96556F3A1A (core DCE95C96556F3A1A)
|
||||||
|
Saving results to: /Users/martin/Library/Application Support/DefaultCompany/luneraria-unity/TestResults.xml
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-run>
|
||||||
15
docs/port/logs/s1/player-benchmark-sylvara-run1.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"build": "OSXPlayer player, 6000.3.20f1, release",
|
||||||
|
"device": "Mac17,2; Apple M5; Apple M5",
|
||||||
|
"graphics_api": "Metal",
|
||||||
|
"resolution": "1920x1080",
|
||||||
|
"vsync": 0,
|
||||||
|
"match": "seed 9 from round 2, army held_sylvara x6, speed 1x, measured after 10 s of combat",
|
||||||
|
// moving units at the start of the first window: w04 11, w06 11, g04 10, g02 9, g06 9, w02 7, sylvara 5, g08 4, g10 4, w08 4, w10 3
|
||||||
|
"windows": [
|
||||||
|
{"view": "overview (zoom 64)", "frames": 3007, "fps": 150.3, "frame_ms_avg": 6.65, "frame_ms_median": 2.90, "frame_ms_p95": 21.10, "frame_ms_p99": 27.60, "frame_ms_max": 37.21, "frames_over_16_7_ms": 304, "of_them_with_simulation_step": 32, "gc_collections": 20, "simulation_ms_per_frame": 0.059, "views_ms_per_frame": 0.193, "ui_ms_per_frame": 0.053, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.29 / median 2.19 / p95 3.14 ms, render thread avg 1.68 / median 1.67 / p95 1.96 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.36 / median 0.25 / p95 18.80 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1708, "fps": 170.8, "frame_ms_avg": 5.86, "frame_ms_median": 2.71, "frame_ms_p95": 20.91, "frame_ms_p99": 28.81, "frame_ms_max": 33.00, "frames_over_16_7_ms": 158, "of_them_with_simulation_step": 12, "gc_collections": 8, "simulation_ms_per_frame": 0.033, "views_ms_per_frame": 0.161, "ui_ms_per_frame": 0.038, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.73 / median 1.65 / p95 2.41 ms, render thread avg 1.31 / median 1.30 / p95 1.53 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.13 / median 0.87 / p95 19.07 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
15
docs/port/logs/s1/player-benchmark-sylvara-run2.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"build": "OSXPlayer player, 6000.3.20f1, release",
|
||||||
|
"device": "Mac17,2; Apple M5; Apple M5",
|
||||||
|
"graphics_api": "Metal",
|
||||||
|
"resolution": "1920x1080",
|
||||||
|
"vsync": 0,
|
||||||
|
"match": "seed 9 from round 2, army held_sylvara x6, speed 1x, measured after 10 s of combat",
|
||||||
|
// moving units at the start of the first window: w04 11, w06 11, g04 10, g02 9, g06 9, w02 7, sylvara 5, g08 4, g10 4, w08 4, w10 3
|
||||||
|
"windows": [
|
||||||
|
{"view": "overview (zoom 64)", "frames": 2972, "fps": 148.5, "frame_ms_avg": 6.73, "frame_ms_median": 2.90, "frame_ms_p95": 21.29, "frame_ms_p99": 27.11, "frame_ms_max": 40.31, "frames_over_16_7_ms": 327, "of_them_with_simulation_step": 16, "gc_collections": 21, "simulation_ms_per_frame": 0.061, "views_ms_per_frame": 0.198, "ui_ms_per_frame": 0.054, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.35 / median 2.24 / p95 3.26 ms, render thread avg 1.71 / median 1.69 / p95 2.03 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.38 / median 0.23 / p95 19.00 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1702, "fps": 170.1, "frame_ms_avg": 5.88, "frame_ms_median": 2.79, "frame_ms_p95": 19.29, "frame_ms_p99": 27.41, "frame_ms_max": 39.90, "frames_over_16_7_ms": 136, "of_them_with_simulation_step": 10, "gc_collections": 8, "simulation_ms_per_frame": 0.032, "views_ms_per_frame": 0.162, "ui_ms_per_frame": 0.039, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.74 / median 1.67 / p95 2.40 ms, render thread avg 1.32 / median 1.30 / p95 1.54 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.15 / median 0.99 / p95 17.65 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
15
docs/port/logs/s1/player-benchmark-sylvara-run3.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"build": "OSXPlayer player, 6000.3.20f1, release",
|
||||||
|
"device": "Mac17,2; Apple M5; Apple M5",
|
||||||
|
"graphics_api": "Metal",
|
||||||
|
"resolution": "1920x1080",
|
||||||
|
"vsync": 0,
|
||||||
|
"match": "seed 9 from round 2, army held_sylvara x6, speed 1x, measured after 10 s of combat",
|
||||||
|
// moving units at the start of the first window: w04 11, w06 11, g04 10, g02 9, g06 9, w02 7, sylvara 5, g08 4, g10 4, w08 4, w10 3
|
||||||
|
"windows": [
|
||||||
|
{"view": "overview (zoom 64)", "frames": 2970, "fps": 148.5, "frame_ms_avg": 6.74, "frame_ms_median": 2.91, "frame_ms_p95": 21.86, "frame_ms_p99": 27.91, "frame_ms_max": 42.25, "frames_over_16_7_ms": 317, "of_them_with_simulation_step": 35, "gc_collections": 21, "simulation_ms_per_frame": 0.061, "views_ms_per_frame": 0.196, "ui_ms_per_frame": 0.054, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.33 / median 2.22 / p95 3.29 ms, render thread avg 1.70 / median 1.69 / p95 2.01 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.39 / median 0.25 / p95 19.50 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1718, "fps": 171.7, "frame_ms_avg": 5.82, "frame_ms_median": 2.68, "frame_ms_p95": 19.50, "frame_ms_p99": 27.41, "frame_ms_max": 34.60, "frames_over_16_7_ms": 144, "of_them_with_simulation_step": 10, "gc_collections": 8, "simulation_ms_per_frame": 0.033, "views_ms_per_frame": 0.163, "ui_ms_per_frame": 0.039, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.73 / median 1.67 / p95 2.46 ms, render thread avg 1.32 / median 1.30 / p95 1.53 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.10 / median 0.51 / p95 17.73 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
11
docs/port/logs/s1/player-smoke.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"build": "OSXPlayer player, 6000.3.20f1, release",
|
||||||
|
"device": "Mac17,2; Apple M5; Apple M5",
|
||||||
|
"graphics_api": "Metal",
|
||||||
|
"resolution": "1920x1080",
|
||||||
|
"seconds": 11.6,
|
||||||
|
"passed": ["Startbildschirm sichtbar", "Partie wartet: Spielcontroller und Treiber aus", "vor dem Start kein HUD", "„Allein spielen“ bedienbar", "Offline: Startbildschirm verborgen", "Offline: Spielcontroller und Treiber laufen", "Offline: HUD sichtbar", "Offline: neue Partie, Sitz 1 (Mid)", "Offline: Planung", "Offline: lokale Sitzung", "„Startaufstellung“ bedienbar", "Startaufstellung gekauft (3 Einheiten)", "„Bereit“ bedienbar", "Bereit: Kampf beginnt", "Kampf: Figuren im Feld", "„Menü“ bedienbar", "Verlassen-Dialog mit „Zum Menü“", "Zurück: Startbildschirm sichtbar", "Zurück: HUD verborgen", "Zurück: Spielcontroller und Treiber aus", "Zurück: Sitzung beendet", "Zurück: keine Figuren im Feld", "Zurück: Kamera ruht", "Zurück: „Allein spielen“ bedienbar", "Zweite Partie: Startbildschirm verborgen, HUD sichtbar", "Zweite Partie: neue Partie in der Planung", "SSAO folgt der Einstellung", "Option „Umgebungsverdeckung“ sichtbar", "Option schaltet SSAO um", "Option stellt SSAO zurück"],
|
||||||
|
"failed": [],
|
||||||
|
"photos": ["00-startbildschirm.jpg", "01-planung.jpg", "02-kampf.jpg", "03-verlassen.jpg", "04-zurueck-im-startbildschirm.jpg", "05-zweite-partie.jpg", "06-optionen.jpg"],
|
||||||
|
"result": "PASS"
|
||||||
|
}
|
||||||
387
docs/port/logs/s1/playmode-results.xml
Normal file
|
|
@ -0,0 +1,387 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="25" result="Passed" total="25" passed="16" failed="0" inconclusive="0" skipped="9" asserts="0" engine-version="3.5.0.0" clr-version="4.0.30319.42000" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:12:36Z" duration="308.9653334">
|
||||||
|
<test-suite type="TestSuite" id="1049" name="luneraria-unity" fullname="luneraria-unity" runstate="Runnable" testcasecount="25" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:12:36Z" duration="308.965333" total="25" passed="16" failed="0" inconclusive="0" skipped="9" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="Assembly" id="1034" name="Luneraria.UI.Tests.PlayMode.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.UI.Tests.PlayMode.dll" runstate="Runnable" testcasecount="23" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:53Z" duration="26.056195" total="23" passed="14" failed="0" inconclusive="0" skipped="9" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99406" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
<property name="EditorOnly" value="False" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1035" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="23" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:53Z" duration="26.055883" total="23" passed="14" failed="0" inconclusive="0" skipped="9" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1036" name="UI" fullname="Luneraria.UI" runstate="Runnable" testcasecount="23" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:53Z" duration="26.055685" total="23" passed="14" failed="0" inconclusive="0" skipped="9" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1037" name="Tests" fullname="Luneraria.UI.Tests" runstate="Runnable" testcasecount="23" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:53Z" duration="26.055361" total="23" passed="14" failed="0" inconclusive="0" skipped="9" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1001" name="ArmyViewsTests" fullname="Luneraria.UI.Tests.ArmyViewsTests" classname="Luneraria.UI.Tests.ArmyViewsTests" runstate="Explicit" testcasecount="1" result="Skipped" label="Explicit" site="SetUp" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:27Z" duration="0.002641" total="1" passed="0" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="Bildwerkzeug: nur gezielt per -testFilter" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
<test-case id="1002" name="Glutpakt" fullname="Luneraria.UI.Tests.ArmyViewsTests.Glutpakt" methodname="Glutpakt" classname="Luneraria.UI.Tests.ArmyViewsTests" runstate="Runnable" seed="1280391378" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1003" name="FigureViewsTests" fullname="Luneraria.UI.Tests.FigureViewsTests" classname="Luneraria.UI.Tests.FigureViewsTests" runstate="Explicit" testcasecount="1" result="Skipped" label="Explicit" site="SetUp" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:27Z" duration="0.000061" total="1" passed="0" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="Bildwerkzeug: nur gezielt per -testFilter" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
<test-case id="1004" name="Kohlenbeisser" fullname="Luneraria.UI.Tests.FigureViewsTests.Kohlenbeisser" methodname="Kohlenbeisser" classname="Luneraria.UI.Tests.FigureViewsTests" runstate="Runnable" seed="1268069011" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1005" name="H1HeroViewsTests" fullname="Luneraria.UI.Tests.H1HeroViewsTests" classname="Luneraria.UI.Tests.H1HeroViewsTests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:29Z" duration="1.992947" total="2" passed="1" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1007" name="SylvaraBilder" fullname="Luneraria.UI.Tests.H1HeroViewsTests.SylvaraBilder" methodname="SylvaraBilder" classname="Luneraria.UI.Tests.H1HeroViewsTests" runstate="Explicit" seed="1027071528" result="Skipped" label="Explicit" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:27Z" duration="0.000154" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="_SKIPREASON" value="Bildwerkzeug: nur gezielt per -testFilter" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1006" name="SylvaraHud" fullname="Luneraria.UI.Tests.H1HeroViewsTests.SylvaraHud" methodname="SylvaraHud" classname="Luneraria.UI.Tests.H1HeroViewsTests" runstate="Runnable" seed="878180846" result="Passed" start-time="2026-10-11 10:07:27Z" end-time="2026-10-11 10:07:29Z" duration="1.990251" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="600000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1008" name="HudCaptureTests" fullname="Luneraria.UI.Tests.HudCaptureTests" classname="Luneraria.UI.Tests.HudCaptureTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:07:29Z" end-time="2026-10-11 10:07:29Z" duration="0.385525" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1009" name="PlanningHudOverTheScene" fullname="Luneraria.UI.Tests.HudCaptureTests.PlanningHudOverTheScene" methodname="PlanningHudOverTheScene" classname="Luneraria.UI.Tests.HudCaptureTests" runstate="Runnable" seed="1982217516" result="Passed" start-time="2026-10-11 10:07:29Z" end-time="2026-10-11 10:07:29Z" duration="0.382265" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="600000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_HUD syncs 1 sync ms 0.157 portraits 14 scale 1 size (1706.67, 960.00)
|
||||||
|
LUNE_HUD region top (x:425.78, y:12.44, width:855.11, height:83.56)
|
||||||
|
LUNE_HUD region system (x:1559.11, y:12.44, width:136.00, height:35.56)
|
||||||
|
LUNE_HUD region left (x:12.44, y:12.44, width:312.00, height:466.67)
|
||||||
|
LUNE_HUD region right (x:1372.44, y:56.00, width:322.67, height:248.00)
|
||||||
|
LUNE_HUD region bottom (x:12.44, y:761.78, width:1432.00, height:186.67)
|
||||||
|
LUNE_HUD region ready (x:1455.11, y:616.00, width:240.00, height:84.44)
|
||||||
|
LUNE_HUD region minimap (x:1455.11, y:708.44, width:240.00, height:240.00)
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1010" name="HudPlaytest" fullname="Luneraria.UI.Tests.HudPlaytest" classname="Luneraria.UI.Tests.HudPlaytest" runstate="Explicit" testcasecount="1" result="Skipped" label="Explicit" site="SetUp" start-time="2026-10-11 10:07:29Z" end-time="2026-10-11 10:07:29Z" duration="0.000097" total="1" passed="0" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="Spieltest: wartet auf Befehlsdateien" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Spieltest: wartet auf Befehlsdateien]]></message>
|
||||||
|
</reason>
|
||||||
|
<test-case id="1011" name="Play" fullname="Luneraria.UI.Tests.HudPlaytest.Play" methodname="Play" classname="Luneraria.UI.Tests.HudPlaytest" runstate="Runnable" seed="1913932782" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="2147483647" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Spieltest: wartet auf Befehlsdateien]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1012" name="HudVerifyTests" fullname="Luneraria.UI.Tests.HudVerifyTests" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" testcasecount="9" result="Passed" start-time="2026-10-11 10:07:29Z" end-time="2026-10-11 10:07:40Z" duration="11.244795" total="9" passed="9" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1013" name="A_Layout" fullname="Luneraria.UI.Tests.HudVerifyTests.A_Layout" methodname="A_Layout" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="2113221857" result="Passed" start-time="2026-10-11 10:07:29Z" end-time="2026-10-11 10:07:33Z" duration="3.984013" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="1800000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY A: 585 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1014" name="B_Status" fullname="Luneraria.UI.Tests.HudVerifyTests.B_Status" methodname="B_Status" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="314955461" result="Passed" start-time="2026-10-11 10:07:33Z" end-time="2026-10-11 10:07:33Z" duration="0.351847" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY B: 21 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1015" name="C_HeroItemsUnits" fullname="Luneraria.UI.Tests.HudVerifyTests.C_HeroItemsUnits" methodname="C_HeroItemsUnits" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="627134692" result="Passed" start-time="2026-10-11 10:07:33Z" end-time="2026-10-11 10:07:34Z" duration="0.656146" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY C: 23 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1016" name="D_ChampionsAndBoss" fullname="Luneraria.UI.Tests.HudVerifyTests.D_ChampionsAndBoss" methodname="D_ChampionsAndBoss" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="1209604441" result="Passed" start-time="2026-10-11 10:07:34Z" end-time="2026-10-11 10:07:35Z" duration="0.490974" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY D: 16 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1017" name="E_ReadyBotsPrivacy" fullname="Luneraria.UI.Tests.HudVerifyTests.E_ReadyBotsPrivacy" methodname="E_ReadyBotsPrivacy" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="943433324" result="Passed" start-time="2026-10-11 10:07:35Z" end-time="2026-10-11 10:07:36Z" duration="1.379796" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY E: 17 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1018" name="F_Input" fullname="Luneraria.UI.Tests.HudVerifyTests.F_Input" methodname="F_Input" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="1046824836" result="Passed" start-time="2026-10-11 10:07:36Z" end-time="2026-10-11 10:07:39Z" duration="3.422346" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY F: 18 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1019" name="G_EventsAndMotion" fullname="Luneraria.UI.Tests.HudVerifyTests.G_EventsAndMotion" methodname="G_EventsAndMotion" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="1272130664" result="Passed" start-time="2026-10-11 10:07:39Z" end-time="2026-10-11 10:07:40Z" duration="0.452101" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY G: 14 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1020" name="H_RatesAndPortraits" fullname="Luneraria.UI.Tests.HudVerifyTests.H_RatesAndPortraits" methodname="H_RatesAndPortraits" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="102762027" result="Passed" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:40Z" duration="0.195196" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY rates planning 18/2s combat 10/2s sync 0.212 ms portraits 15
|
||||||
|
LUNE_VERIFY H: 5 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1021" name="I_Field" fullname="Luneraria.UI.Tests.HudVerifyTests.I_Field" methodname="I_Field" classname="Luneraria.UI.Tests.HudVerifyTests" runstate="Runnable" seed="1558362320" result="Passed" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:40Z" duration="0.308600" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="900000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_VERIFY I: 19 checks, 0 failed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1022" name="HudViewsTests" fullname="Luneraria.UI.Tests.HudViewsTests" classname="Luneraria.UI.Tests.HudViewsTests" runstate="Explicit" testcasecount="4" result="Skipped" label="Explicit" site="SetUp" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:40Z" duration="0.000237" total="4" passed="0" failed="0" inconclusive="0" skipped="4" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_SKIPREASON" value="Bildwerkzeug: nur gezielt per -testFilter" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
<test-case id="1025" name="Motifs_1280x720" fullname="Luneraria.UI.Tests.HudViewsTests.Motifs_1280x720" methodname="Motifs_1280x720" classname="Luneraria.UI.Tests.HudViewsTests" runstate="Runnable" seed="2102668946" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="1800000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1023" name="Motifs_1920x1080" fullname="Luneraria.UI.Tests.HudViewsTests.Motifs_1920x1080" methodname="Motifs_1920x1080" classname="Luneraria.UI.Tests.HudViewsTests" runstate="Runnable" seed="1762142832" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="1800000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1026" name="Motifs_2560x1080" fullname="Luneraria.UI.Tests.HudViewsTests.Motifs_2560x1080" methodname="Motifs_2560x1080" classname="Luneraria.UI.Tests.HudViewsTests" runstate="Runnable" seed="908167343" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="1800000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1024" name="Motifs_2560x1440" fullname="Luneraria.UI.Tests.HudViewsTests.Motifs_2560x1440" methodname="Motifs_2560x1440" classname="Luneraria.UI.Tests.HudViewsTests" runstate="Runnable" seed="1429993439" result="Skipped" label="Explicit" site="Parent" start-time="0001-01-01 00:00:00Z" end-time="0001-01-01 00:00:00Z" duration="0.000000" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="1800000" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1027" name="S1RulesViewsTests" fullname="Luneraria.UI.Tests.S1RulesViewsTests" classname="Luneraria.UI.Tests.S1RulesViewsTests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:42Z" duration="1.638953" total="2" passed="1" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1029" name="S1Bilder" fullname="Luneraria.UI.Tests.S1RulesViewsTests.S1Bilder" methodname="S1Bilder" classname="Luneraria.UI.Tests.S1RulesViewsTests" runstate="Explicit" seed="1612044270" result="Skipped" label="Explicit" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:40Z" duration="0.000001" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="600000" />
|
||||||
|
<property name="_SKIPREASON" value="Bildwerkzeug: nur gezielt per -testFilter" />
|
||||||
|
</properties>
|
||||||
|
<reason>
|
||||||
|
<message><![CDATA[Bildwerkzeug: nur gezielt per -testFilter]]></message>
|
||||||
|
</reason>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1028" name="S1Hud" fullname="Luneraria.UI.Tests.S1RulesViewsTests.S1Hud" methodname="S1Hud" classname="Luneraria.UI.Tests.S1RulesViewsTests" runstate="Runnable" seed="965416766" result="Passed" start-time="2026-10-11 10:07:40Z" end-time="2026-10-11 10:07:42Z" duration="1.637410" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="600000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1030" name="StartScreenTests" fullname="Luneraria.UI.Tests.StartScreenTests" classname="Luneraria.UI.Tests.StartScreenTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:07:42Z" end-time="2026-10-11 10:07:53Z" duration="10.716113" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1031" name="StartScreenLeadsIntoTheMatchAndBack" fullname="Luneraria.UI.Tests.StartScreenTests.StartScreenLeadsIntoTheMatchAndBack" methodname="StartScreenLeadsIntoTheMatchAndBack" classname="Luneraria.UI.Tests.StartScreenTests" runstate="Runnable" seed="966778946" result="Passed" start-time="2026-10-11 10:07:42Z" end-time="2026-10-11 10:07:53Z" duration="10.712687" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="300000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_SMOKE ok Startbildschirm sichtbar
|
||||||
|
LUNE_SMOKE ok Partie wartet: Spielcontroller und Treiber aus
|
||||||
|
LUNE_SMOKE ok vor dem Start kein HUD
|
||||||
|
LUNE_SMOKE ok „Allein spielen“ bedienbar
|
||||||
|
LUNE_SMOKE ok Offline: Startbildschirm verborgen
|
||||||
|
LUNE_SMOKE ok Offline: Spielcontroller und Treiber laufen
|
||||||
|
LUNE_SMOKE ok Offline: HUD sichtbar
|
||||||
|
LUNE_SMOKE ok Offline: neue Partie, Sitz 1 (Mid)
|
||||||
|
LUNE_SMOKE ok Offline: Planung
|
||||||
|
LUNE_SMOKE ok Offline: lokale Sitzung
|
||||||
|
LUNE_SMOKE ok „Startaufstellung“ bedienbar
|
||||||
|
LUNE_SMOKE ok Startaufstellung gekauft (3 Einheiten)
|
||||||
|
LUNE_SMOKE ok „Bereit“ bedienbar
|
||||||
|
LUNE_SMOKE ok Bereit: Kampf beginnt
|
||||||
|
LUNE_SMOKE ok Kampf: Figuren im Feld
|
||||||
|
LUNE_SMOKE ok „Menü“ bedienbar
|
||||||
|
LUNE_SMOKE ok Verlassen-Dialog mit „Zum Menü“
|
||||||
|
LUNE_SMOKE ok Zurück: Startbildschirm sichtbar
|
||||||
|
LUNE_SMOKE ok Zurück: HUD verborgen
|
||||||
|
LUNE_SMOKE ok Zurück: Spielcontroller und Treiber aus
|
||||||
|
LUNE_SMOKE ok Zurück: Sitzung beendet
|
||||||
|
LUNE_SMOKE ok Zurück: keine Figuren im Feld
|
||||||
|
LUNE_SMOKE ok Zurück: Kamera ruht
|
||||||
|
LUNE_SMOKE ok Zurück: „Allein spielen“ bedienbar
|
||||||
|
LUNE_SMOKE ok Zweite Partie: Startbildschirm verborgen, HUD sichtbar
|
||||||
|
LUNE_SMOKE ok Zweite Partie: neue Partie in der Planung
|
||||||
|
LUNE_SMOKE_PASS 26 ok, 0 Fehler
|
||||||
|
LUNE_START_SCREEN 26 checks passed
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1032" name="UiToolkitProbeTests" fullname="Luneraria.UI.Tests.UiToolkitProbeTests" classname="Luneraria.UI.Tests.UiToolkitProbeTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:07:53Z" duration="0.072980" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1033" name="PanelRendersIntoTexture" fullname="Luneraria.UI.Tests.UiToolkitProbeTests.PanelRendersIntoTexture" methodname="PanelRendersIntoTexture" classname="Luneraria.UI.Tests.UiToolkitProbeTests" runstate="Runnable" seed="618367356" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:07:53Z" duration="0.072154" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_SPIKE corner RGBA(0.565, 0.635, 0.506, 1.000) panel RGBA(0.196, 0.227, 0.204, 1.000) layout (x:12.44, y:12.44, width:760.00, height:530.67)
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="Assembly" id="1042" name="Luneraria.View.Tests.PlayMode.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.View.Tests.PlayMode.dll" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:12:36Z" duration="282.906056" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="99406" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
<property name="EditorOnly" value="False" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1043" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:12:36Z" duration="282.905397" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1044" name="View" fullname="Luneraria.View" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:12:36Z" duration="282.905208" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1045" name="Tests" fullname="Luneraria.View.Tests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:12:36Z" duration="282.904955" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1038" name="SceneFrameRateTests" fullname="Luneraria.View.Tests.SceneFrameRateTests" classname="Luneraria.View.Tests.SceneFrameRateTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:09:43Z" duration="110.490753" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1039" name="SceneMatchIsTheSameAtRealFrameRates" fullname="Luneraria.View.Tests.SceneFrameRateTests.SceneMatchIsTheSameAtRealFrameRates" methodname="SceneMatchIsTheSameAtRealFrameRates" classname="Luneraria.View.Tests.SceneFrameRateTests" runstate="Runnable" seed="654477913" result="Passed" start-time="2026-10-11 10:07:53Z" end-time="2026-10-11 10:09:43Z" duration="110.489249" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_SCENE_FPS 30 fps: 816 frames in 27.2 s = 30.0 fps, up to 17 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
LUNE_SCENE_FPS 60 fps: 1626 frames in 27.1 s = 60.0 fps, up to 10 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
LUNE_SCENE_FPS 120 fps: 3225 frames in 26.9 s = 119.8 fps, up to 7 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
LUNE_SCENE_FPS unlimited: 85223 frames in 26.8 s = 3177.6 fps, up to 6 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1040" name="VerticalFunctionTests" fullname="Luneraria.View.Tests.VerticalFunctionTests" classname="Luneraria.View.Tests.VerticalFunctionTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:09:43Z" end-time="2026-10-11 10:12:36Z" duration="172.413712" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1041" name="BotMatchesRunVisiblyAndMatchTheCore" fullname="Luneraria.View.Tests.VerticalFunctionTests.BotMatchesRunVisiblyAndMatchTheCore" methodname="BotMatchesRunVisiblyAndMatchTheCore" classname="Luneraria.View.Tests.VerticalFunctionTests" runstate="Runnable" seed="2093396960" result="Passed" start-time="2026-10-11 10:09:43Z" end-time="2026-10-11 10:12:36Z" duration="172.408691" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_JOINTYPE" value="UnityCombinatorial" />
|
||||||
|
<property name="Timeout" value="3600000" />
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[LUNE_U2D shot 01-runde1-aufmarsch round 1 Combat
|
||||||
|
LUNE_U2D shot 02-runde1-mid-kampf round 1 Combat
|
||||||
|
LUNE_U2D shot 03-turm-faellt round 7 Combat
|
||||||
|
LUNE_U2D shot 04-turm-ruine round 7 Combat
|
||||||
|
LUNE_U2D shot 05-grotte-bosskampf round 8 Combat
|
||||||
|
LUNE_U2D shot 06-held-kaempft round 9 Combat
|
||||||
|
LUNE_U2D shot 07-boss-auf-der-lane round 9 Combat
|
||||||
|
LUNE_U2D shot 08-ancient-faellt round 9 Over
|
||||||
|
LUNE_U2D shot 09-ancient-ruine round 9 Over
|
||||||
|
LUNE_U2D shot 10-ende-uebersicht round 9 Over
|
||||||
|
LUNE_U2D shot 11-welle21-uebersicht round 21 Combat
|
||||||
|
LUNE_U2D shot 12-welle21-kampf round 21 Combat
|
||||||
|
LUNE_U2D shot 13-ancient-rot-faellt round 22 Over
|
||||||
|
LUNE_U2D shot 14-welle-ende round 22 Over
|
||||||
|
Saving results to: /Users/martin/Library/Application Support/DefaultCompany/luneraria-unity/TestResults.xml
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-run>
|
||||||
11
docs/port/logs/s1/scene-frame-rates.json
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"match": "seed 2, bots, speed 24x",
|
||||||
|
"core_hash": "DCE95C96556F3A1A",
|
||||||
|
"core_steps": 12690,
|
||||||
|
"runs": [
|
||||||
|
{"target": "30 fps", "frames": 816, "seconds": 27.178, "measured_fps": 30.024, "max_steps_per_frame": 17, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "60 fps", "frames": 1626, "seconds": 27.086, "measured_fps": 60.031, "max_steps_per_frame": 10, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "120 fps", "frames": 3225, "seconds": 26.91, "measured_fps": 119.845, "max_steps_per_frame": 7, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "unlimited", "frames": 85223, "seconds": 26.82, "measured_fps": 3177.628, "max_steps_per_frame": 6, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
docs/port/logs/s1/vertical-function-test.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"runs": [
|
||||||
|
{"label": "match-seed2", "seed": 2, "start_round": 1, "extra_gold": 0, "speed": 6, "winner": 1, "end_note": "Ancient von Blau zerstört.", "steps": 12690, "hash": "DCE95C96556F3A1A", "core_hash": "DCE95C96556F3A1A", "same_as_core": true,
|
||||||
|
"units_alive_max": 84, "heroes_alive_max": 4, "boss_log": ["round 8: won team 1 ", "round 9: deployed team 1 bot"],
|
||||||
|
"rounds": [{"round": 1, "combat_seconds": 44.9, "lost": [], "boss": "none", "end": "cleared"}, {"round": 2, "combat_seconds": 57.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 3, "combat_seconds": 54.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 4, "combat_seconds": 62.9, "lost": [], "boss": "none", "end": "cleared"}, {"round": 5, "combat_seconds": 60.7, "lost": [], "boss": "none", "end": "cleared"}, {"round": 6, "combat_seconds": 62.1, "lost": [], "boss": "none", "end": "cleared"}, {"round": 7, "combat_seconds": 66.5, "lost": ["blau_bot_outer"], "boss": "none", "end": "cleared"}, {"round": 8, "combat_seconds": 89.65, "lost": ["rot_top_outer", "rot_top_inner"], "boss": "won", "end": "cleared"}, {"round": 9, "combat_seconds": 136.55, "lost": ["blau_bot_inner", "blau_base_2", "blau_base_1", "blau_ancient"], "boss": "deployed", "end": "core"}],
|
||||||
|
"events": ["round 7: blau_bot_outer destroyed", "round 8: rot_top_outer destroyed", "round 8: rot_top_inner destroyed", "round 8: grotto boss won", "round 9: blau_bot_inner destroyed", "round 9: blau_base_2 destroyed", "round 9: blau_base_1 destroyed", "round 9: blau_ancient destroyed", "round 9: grotto boss deployed"],
|
||||||
|
"shots": ["01-runde1-aufmarsch", "02-runde1-mid-kampf", "03-turm-faellt", "04-turm-ruine", "05-grotte-bosskampf", "06-held-kaempft", "07-boss-auf-der-lane", "08-ancient-faellt", "09-ancient-ruine", "10-ende-uebersicht"],
|
||||||
|
"performance": null},
|
||||||
|
{"label": "match-seed9-round21", "seed": 9, "start_round": 21, "extra_gold": 4000, "speed": 6, "winner": 0, "end_note": "Ancient von Rot zerstört.", "steps": 4195, "hash": "7B8E43AEFC0D60C5", "core_hash": "7B8E43AEFC0D60C5", "same_as_core": true,
|
||||||
|
"units_alive_max": 125, "heroes_alive_max": 6, "boss_log": [],
|
||||||
|
"rounds": [{"round": 21, "combat_seconds": 121.8, "lost": ["rot_mid_outer", "rot_top_outer", "rot_mid_inner"], "boss": "none", "end": "cleared"}, {"round": 22, "combat_seconds": 87.95, "lost": ["rot_top_inner", "rot_base_1", "blau_bot_outer", "rot_base_2", "rot_ancient"], "boss": "none", "end": "core"}],
|
||||||
|
"events": ["round 21: rot_mid_outer destroyed", "round 21: rot_top_outer destroyed", "round 21: rot_mid_inner destroyed", "round 22: rot_top_inner destroyed", "round 22: rot_base_1 destroyed", "round 22: blau_bot_outer destroyed", "round 22: rot_base_2 destroyed", "round 22: rot_ancient destroyed"],
|
||||||
|
"shots": ["11-welle21-uebersicht", "12-welle21-kampf", "13-ancient-rot-faellt", "14-welle-ende"],
|
||||||
|
"performance": {"window": "20 s real time at 1x speed, unlimited frame rate, round 21", "frames": 48702, "fps": 2435.099, "frame_ms_avg": 0.411, "frame_ms_p95": 0.418, "simulation_ms_per_frame": 0.004, "views_ms_per_frame": 0.129, "units_alive_max": 116}}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
docs/port/screenshots/s1/ablauf/01-runde1-aufmarsch.jpg
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
docs/port/screenshots/s1/ablauf/02-runde1-mid-kampf.jpg
Normal file
|
After Width: | Height: | Size: 599 KiB |
BIN
docs/port/screenshots/s1/ablauf/03-turm-faellt.jpg
Normal file
|
After Width: | Height: | Size: 536 KiB |
BIN
docs/port/screenshots/s1/ablauf/04-turm-ruine.jpg
Normal file
|
After Width: | Height: | Size: 527 KiB |
BIN
docs/port/screenshots/s1/ablauf/05-grotte-bosskampf.jpg
Normal file
|
After Width: | Height: | Size: 598 KiB |
BIN
docs/port/screenshots/s1/ablauf/06-held-kaempft.jpg
Normal file
|
After Width: | Height: | Size: 615 KiB |
BIN
docs/port/screenshots/s1/ablauf/07-boss-auf-der-lane.jpg
Normal file
|
After Width: | Height: | Size: 566 KiB |
BIN
docs/port/screenshots/s1/ablauf/08-ancient-faellt.jpg
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/port/screenshots/s1/ablauf/09-ancient-ruine.jpg
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/port/screenshots/s1/ablauf/10-ende-uebersicht.jpg
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/port/screenshots/s1/ablauf/11-welle21-uebersicht.jpg
Normal file
|
After Width: | Height: | Size: 580 KiB |
BIN
docs/port/screenshots/s1/ablauf/12-welle21-kampf.jpg
Normal file
|
After Width: | Height: | Size: 638 KiB |
BIN
docs/port/screenshots/s1/ablauf/13-ancient-rot-faellt.jpg
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
docs/port/screenshots/s1/ablauf/14-welle-ende.jpg
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
docs/port/screenshots/s1/spiel/00-startbildschirm-regeln.jpg
Normal file
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 767 KiB |
BIN
docs/port/screenshots/s1/spiel/02-kaserne-tooltip.jpg
Normal file
|
After Width: | Height: | Size: 746 KiB |
BIN
docs/port/screenshots/s1/spiel/03-rueckkauf.jpg
Normal file
|
After Width: | Height: | Size: 622 KiB |
BIN
docs/port/screenshots/s1/spiel/04-verstaerkte-welle-mid.jpg
Normal file
|
After Width: | Height: | Size: 720 KiB |
|
|
@ -17,6 +17,8 @@ public static class Program
|
||||||
|
|
||||||
public static int Main(string[] args)
|
public static int Main(string[] args)
|
||||||
{
|
{
|
||||||
|
if (args.Length > 0 && args[0] == "s1")
|
||||||
|
return RulesComparison(args.Length > 1 ? int.Parse(args[1], CultureInfo.InvariantCulture) : 60);
|
||||||
int seeds = args.Length > 0 ? int.Parse(args[0], CultureInfo.InvariantCulture) : 40;
|
int seeds = args.Length > 0 ? int.Parse(args[0], CultureInfo.InvariantCulture) : 40;
|
||||||
BotProfile reference = ContentData.BotProfiles["ausgewogen"];
|
BotProfile reference = ContentData.BotProfiles["ausgewogen"];
|
||||||
foreach (string hero in Heroes)
|
foreach (string hero in Heroes)
|
||||||
|
|
@ -69,4 +71,61 @@ public static class Program
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// S1: plain bot matches (seed rotation of profiles) under reference and extended rules:
|
||||||
|
/// match length, winners, how often each extended mechanic acts, and how often the team that
|
||||||
|
/// first took a tower also won (snowball).
|
||||||
|
/// </summary>
|
||||||
|
static int RulesComparison(int seeds)
|
||||||
|
{
|
||||||
|
Console.WriteLine("rules; matches; blue wins; red wins; draws; open; mean rounds; min; max; first tower wins; champion income/seat; barracks waves; exhausted heroes; buybacks; blessings; sylvara seats; sylvara wins");
|
||||||
|
foreach (bool extended in new[] { false, true })
|
||||||
|
{
|
||||||
|
int blue = 0, red = 0, draws = 0, open = 0, rounds = 0, min = 999, max = 0, firstTowerWins = 0, firstTowerGames = 0;
|
||||||
|
long income = 0; int barracks = 0, exhausted = 0, buybacks = 0, blessings = 0, sylvaraSeats = 0, sylvaraWins = 0;
|
||||||
|
for (int seed = 1; seed <= seeds; seed++)
|
||||||
|
{
|
||||||
|
var match = new LuneMatch();
|
||||||
|
match.Setup(new MatchOptions { Seed = seed, HumanSlot = -1, Extended = extended });
|
||||||
|
int firstTower = -1;
|
||||||
|
while (match.Phase != Phase.Over && match.RoundNumber <= 80)
|
||||||
|
{
|
||||||
|
for (int slot = 0; slot < 6; slot++)
|
||||||
|
{
|
||||||
|
bool wasExhausted = match.Players[slot].Hero != null && match.Players[slot].Hero.Exhausted;
|
||||||
|
LuneBot.Plan(match, slot);
|
||||||
|
if (wasExhausted && match.Players[slot].Draft.Hero != null && !match.Players[slot].Draft.Hero.Exhausted)
|
||||||
|
buybacks++;
|
||||||
|
}
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
foreach (string lane in LuneMap.Lanes)
|
||||||
|
if (extended && match.BarracksBroken(team, lane)) barracks++;
|
||||||
|
match.BeginCombat();
|
||||||
|
match.FinishCombat();
|
||||||
|
foreach (Player player in match.Players)
|
||||||
|
if (player.Hero != null && player.Hero.Exhausted) exhausted++;
|
||||||
|
if (firstTower < 0 && match.LastSettlement != null && match.LastSettlement.BuildingsLost.Count > 0)
|
||||||
|
firstTower = match.LastSettlement.BuildingsLost[0].StartsWith("rot") ? 0 : 1;
|
||||||
|
}
|
||||||
|
int length = match.RoundNumber;
|
||||||
|
rounds += length; min = Math.Min(min, length); max = Math.Max(max, length);
|
||||||
|
if (match.Phase != Phase.Over) open++;
|
||||||
|
else if (match.Winner == 0) blue++;
|
||||||
|
else if (match.Winner == 1) red++;
|
||||||
|
else draws++;
|
||||||
|
if (firstTower >= 0 && match.Phase == Phase.Over) { firstTowerGames++; if (match.Winner == firstTower) firstTowerWins++; }
|
||||||
|
foreach (Player player in match.Players)
|
||||||
|
{
|
||||||
|
income += player.IncomeBonus;
|
||||||
|
if (player.Hero != null && player.Hero.HeroId == "held_sylvara") { sylvaraSeats++; if (match.Winner == player.Team) sylvaraWins++; }
|
||||||
|
}
|
||||||
|
blessings += match.Boss.Blessing[0] + match.Boss.Blessing[1];
|
||||||
|
}
|
||||||
|
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}; {1}; {2}; {3}; {4}; {5}; {6:F1}; {7}; {8}; {9}/{10}; {11:F1}; {12}; {13}; {14}; {15}; {16}; {17}",
|
||||||
|
extended ? "erweitert" : "referenz", seeds, blue, red, draws, open, (double)rounds / seeds, min, max, firstTowerWins, firstTowerGames,
|
||||||
|
(double)income / (seeds * 6), barracks, exhausted, buybacks, blessings, sylvaraSeats, sylvaraWins));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||