S2 Glyphe, Item-Rezepte und Ancient-Stufen (Dota 2, LoL, Legion TD 2)
Hinter MatchOptions.Extended: Glyphe als Teamwahl einer Lane (Türme 5 s unverwundbar, sobald einer unter 50 % fällt; 4 Runden Abklingzeit nur nach Auslösen), vier Rezepte (Bollwerk, Glutsturm, Quellherz, Splitterkrone), Ancient-Stufen 1-3 (150 x Stufe, Reparatur 1.000, Schuss 45/75/110), Mid-Bots jagen bei sicherer Lane die Grotte. Teampanel mit Glyphe und Ancient, Rezepte im Shop (TEILE FEHLEN), Glyphen-Ring und Ancient-Schüsse. Kern 566/566 (s2 35), EditMode 32/32, PlayMode 17/17, Verify 718/0, Selbsttest 30/30, Golden 0, Fingerabdruck unverändert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
@ -126,9 +126,17 @@ namespace Luneraria.Core
|
||||||
new ItemSpec { Id = "rankenmantel", Name = "Rankenmantel", Cost = 200, Text = "+10 % Schadensminderung", Reduction = 0.1 },
|
new ItemSpec { Id = "rankenmantel", Name = "Rankenmantel", Cost = 200, Text = "+10 % Schadensminderung", Reduction = 0.1 },
|
||||||
new ItemSpec { Id = "funkenfokus", Name = "Funkenfokus", Cost = 200, Text = "+15 % Angriffstempo", AttackSpeed = 0.15 },
|
new ItemSpec { Id = "funkenfokus", Name = "Funkenfokus", Cost = 200, Text = "+15 % Angriffstempo", AttackSpeed = 0.15 },
|
||||||
new ItemSpec { Id = "quellamulett", Name = "Quellamulett", Cost = 220, Text = "Regeneriert 1,5 % der Lebenspunkte pro Sekunde", Regen = 0.015 },
|
new ItemSpec { Id = "quellamulett", Name = "Quellamulett", Cost = 220, Text = "Regeneriert 1,5 % der Lebenspunkte pro Sekunde", Regen = 0.015 },
|
||||||
new ItemSpec { Id = "kristallsplitter", Name = "Kristallsplitter", Cost = 260, Text = "Jeder Treffer: 25 % an bis zu 2 Gegner in 1,5 m · ab Stufe 3", Splash = 0.25, MinLevel = 3 }
|
new ItemSpec { Id = "kristallsplitter", Name = "Kristallsplitter", Cost = 260, Text = "Jeder Treffer: 25 % an bis zu 2 Gegner in 1,5 m · ab Stufe 3", Splash = 0.25, MinLevel = 3 },
|
||||||
|
// S2 recipes (extended rules only; Dota 2 / League of Legends): two items plus a fee become one stronger item.
|
||||||
|
new ItemSpec { Id = "bollwerk", Name = "Bollwerk", Cost = 500, Text = "Rezept: Basaltschild + Rankenmantel + 120 · +600 Lebenspunkte, +15 % Schadensminderung", HpFlat = 600.0, Reduction = 0.15, Parts = new[] { "basaltschild", "rankenmantel" }, RecipeFee = 120 },
|
||||||
|
new ItemSpec { Id = "glutsturm", Name = "Glutsturm", Cost = 520, Text = "Rezept: Glutklinge + Funkenfokus + 140 · +25 % Schaden, +22 % Angriffstempo", Damage = 0.25, AttackSpeed = 0.22, Parts = new[] { "glutklinge", "funkenfokus" }, RecipeFee = 140 },
|
||||||
|
new ItemSpec { Id = "quellherz", Name = "Quellherz", Cost = 520, Text = "Rezept: Quellamulett + Basaltschild + 120 · +450 Lebenspunkte, regeneriert 2,2 % pro Sekunde", HpFlat = 450.0, Regen = 0.022, Parts = new[] { "quellamulett", "basaltschild" }, RecipeFee = 120 },
|
||||||
|
new ItemSpec { Id = "splitterkrone", Name = "Splitterkrone", Cost = 600, Text = "Rezept: Kristallsplitter + Glutklinge + 160 · +20 % Schaden; jeder Treffer 35 % an bis zu 2 Gegner in 1,5 m · ab Stufe 5", Damage = 0.20, Splash = 0.35, MinLevel = 5, Parts = new[] { "kristallsplitter", "glutklinge" }, RecipeFee = 160 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// <summary>Items of the Godot reference; ContentTests compares exactly these, the S2 recipes follow them.</summary>
|
||||||
|
public static readonly string[] ReferenceItems = { "basaltschild", "glutklinge", "rankenmantel", "funkenfokus", "quellamulett", "kristallsplitter" };
|
||||||
|
|
||||||
public static readonly OrderedMap<string, ChampionSpec> Champions = Ordered(
|
public static readonly OrderedMap<string, ChampionSpec> Champions = Ordered(
|
||||||
new ChampionSpec { Id = "ch_schutz", Name = "Rostschieber", RoleText = "Schutz", Model = "s02", Cost = 150, Base = "s02", Text = "Schildträger: startet mit Schild, gibt alle 6 s bis zu 4 Verbündeten in 4 m einen Schild von 120.", ViewScale = 1.25 },
|
new ChampionSpec { Id = "ch_schutz", Name = "Rostschieber", RoleText = "Schutz", Model = "s02", Cost = 150, Base = "s02", Text = "Schildträger: startet mit Schild, gibt alle 6 s bis zu 4 Verbündeten in 4 m einen Schild von 120.", ViewScale = 1.25 },
|
||||||
new ChampionSpec { Id = "ch_druck", Name = "Dunstsänger", RoleText = "Druck auf Unterstützer", Model = "s03", Cost = 150, Base = "s03", Text = "Sucht Unterstützer und Fernkämpfer und verlangsamt deren Angriffe.", ViewScale = 1.25, Priority = "support" },
|
new ChampionSpec { Id = "ch_druck", Name = "Dunstsänger", RoleText = "Druck auf Unterstützer", Model = "s03", Cost = 150, Base = "s03", Text = "Sucht Unterstützer und Fernkämpfer und verlangsamt deren Angriffe.", ViewScale = 1.25, Priority = "support" },
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,11 @@ namespace Luneraria.Core
|
||||||
public double Regen;
|
public double Regen;
|
||||||
public double Splash;
|
public double Splash;
|
||||||
public int MinLevel = 1;
|
public int MinLevel = 1;
|
||||||
|
/// <summary>S2 recipe items (extended rules): the two items it is made of and the fee on top; Cost is the sum.</summary>
|
||||||
|
public string[] Parts = new string[0];
|
||||||
|
public int RecipeFee;
|
||||||
|
|
||||||
|
public bool IsRecipe { get { return Parts.Length > 0; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ChampionSpec
|
public sealed class ChampionSpec
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,22 @@ namespace Luneraria.Core
|
||||||
/// <summary>Moon soul (all stacks): heroes regenerate this share of their health per second.</summary>
|
/// <summary>Moon soul (all stacks): heroes regenerate this share of their health per second.</summary>
|
||||||
public double SoulRegen = 0.01;
|
public double SoulRegen = 0.01;
|
||||||
|
|
||||||
|
// S2: extended rules.
|
||||||
|
/// <summary>Dota 2 glyph: a planned lane's towers turn invulnerable for this long once one falls below half health.</summary>
|
||||||
|
public double GlyphSeconds = 5.0;
|
||||||
|
public double GlyphThreshold = 0.5;
|
||||||
|
public int GlyphCooldown = 4;
|
||||||
|
/// <summary>Legion TD 2 king: Ancient levels bought by the team's players; price per level, attack and repair per level.</summary>
|
||||||
|
public int CoreLevelMax = 3;
|
||||||
|
public int CoreLevelPrice = 150;
|
||||||
|
public double[] CoreDamage = { 0.0, 45.0, 75.0, 110.0 };
|
||||||
|
public double CoreRange = 7.5;
|
||||||
|
public double CoreRepair = 1000.0;
|
||||||
|
|
||||||
public static LuneBalance Default() { return new LuneBalance(); }
|
public static LuneBalance Default() { return new LuneBalance(); }
|
||||||
|
|
||||||
|
public int CoreUpgradeCost(int level) { return CoreLevelPrice * (level + 1); }
|
||||||
|
|
||||||
public int BuybackCost(int level) { return BuybackBase + BuybackPerLevel * level; }
|
public int BuybackCost(int level) { return BuybackBase + BuybackPerLevel * level; }
|
||||||
|
|
||||||
public int RoundIncome(int roundNumber)
|
public int RoundIncome(int roundNumber)
|
||||||
|
|
|
||||||
|
|
@ -624,6 +624,10 @@ namespace Luneraria.Core
|
||||||
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)
|
if (source.Blessing != null && source.Blessing.Length == 2)
|
||||||
MatchState.Boss.Blessing = (int[])source.Blessing.Clone();
|
MatchState.Boss.Blessing = (int[])source.Blessing.Clone();
|
||||||
|
if (source.GlyphReady != null && source.GlyphReady.Length == 2)
|
||||||
|
MatchState.Boss.GlyphReady = (int[])source.GlyphReady.Clone();
|
||||||
|
if (source.CoreLevel != null && source.CoreLevel.Length == 2)
|
||||||
|
MatchState.Boss.CoreLevel = (int[])source.CoreLevel.Clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyLobby(List<LobbyEntry> entries)
|
public void ApplyLobby(List<LobbyEntry> entries)
|
||||||
|
|
@ -861,7 +865,9 @@ 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()
|
Blessing = (int[])MatchState.Boss.Blessing.Clone(),
|
||||||
|
GlyphReady = (int[])MatchState.Boss.GlyphReady.Clone(),
|
||||||
|
CoreLevel = (int[])MatchState.Boss.CoreLevel.Clone()
|
||||||
};
|
};
|
||||||
if (MatchState.Combat != null)
|
if (MatchState.Combat != null)
|
||||||
state.DestroyedCores.AddRange(MatchState.Combat.DestroyedCores);
|
state.DestroyedCores.AddRange(MatchState.Combat.DestroyedCores);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ namespace Luneraria.Core
|
||||||
// Items and champions are planned before the army so the roster cannot drain the reserve.
|
// Items and champions are planned before the army so the roster cannot drain the reserve.
|
||||||
PlanItems(match, slot, profile, 110);
|
PlanItems(match, slot, profile, 110);
|
||||||
PlanChampion(match, slot, profile);
|
PlanChampion(match, slot, profile);
|
||||||
|
// S2 team actions come before the army too, so a threatened base gets its Ancient level.
|
||||||
|
if (match.Extended)
|
||||||
|
PlanTeam(match, slot);
|
||||||
for (int guard = 0; guard < 40; guard++)
|
for (int guard = 0; guard < 40; guard++)
|
||||||
{
|
{
|
||||||
List<RosterEntry> roster = player.Draft.Roster;
|
List<RosterEntry> roster = player.Draft.Roster;
|
||||||
|
|
@ -90,9 +93,60 @@ namespace Luneraria.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// S2 team actions (extended rules only): hold the glyph for the own lane when one of its
|
||||||
|
/// towers is below 60 %, and raise the Ancient once the base is under threat.
|
||||||
|
/// </summary>
|
||||||
|
static void PlanTeam(LuneMatch match, int slot)
|
||||||
|
{
|
||||||
|
Player player = match.Players[slot];
|
||||||
|
if (match.RoundNumber >= match.Boss.GlyphReady[player.Team])
|
||||||
|
{
|
||||||
|
foreach (BuildingDef definition in match.Map.Buildings)
|
||||||
|
{
|
||||||
|
if (definition.Team == player.Team && definition.Lane == player.Lane && definition.Tier != "core"
|
||||||
|
&& match.BuildingHp[definition.Id] > 0.0 && match.BuildingHp[definition.Id] < definition.MaxHp * 0.6)
|
||||||
|
{
|
||||||
|
match.Submit(slot, Command.Glyph(player.Lane));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int level = match.Boss.CoreLevel[player.Team] + player.Draft.CoreInvest;
|
||||||
|
bool threatened = false;
|
||||||
|
foreach (BuildingDef definition in match.Map.Buildings)
|
||||||
|
{
|
||||||
|
if (definition.Team == player.Team && definition.Tier == "inner" && match.BuildingHp[definition.Id] <= 0.0)
|
||||||
|
threatened = true;
|
||||||
|
}
|
||||||
|
if (threatened && level < match.Balance.CoreLevelMax && player.Draft.Gold >= match.Balance.CoreUpgradeCost(level))
|
||||||
|
match.Submit(slot, Command.CoreUpgrade());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>S2: both own towers of the player's lane stand above 70 % (an extra grotto hunter leaves only a safe lane).</summary>
|
||||||
|
static bool LaneSafe(LuneMatch match, Player player)
|
||||||
|
{
|
||||||
|
foreach (BuildingDef definition in match.Map.Buildings)
|
||||||
|
{
|
||||||
|
if (definition.Team == player.Team && definition.Lane == player.Lane && (definition.Tier == "outer" || definition.Tier == "inner")
|
||||||
|
&& match.BuildingHp[definition.Id] < definition.MaxHp * 0.7)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void PlanItems(LuneMatch match, int slot, BotProfile profile, int reserve)
|
static void PlanItems(LuneMatch match, int slot, BotProfile profile, int reserve)
|
||||||
{
|
{
|
||||||
HeroState hero = match.Players[slot].Draft.Hero;
|
HeroState hero = match.Players[slot].Draft.Hero;
|
||||||
|
// S2 recipes: combine owned parts first (frees a slot), one per planning.
|
||||||
|
if (hero != null && match.Extended)
|
||||||
|
{
|
||||||
|
foreach (ItemSpec recipe in ContentData.Items.Values)
|
||||||
|
{
|
||||||
|
if (recipe.IsRecipe && match.Players[slot].Draft.Gold >= recipe.RecipeFee + reserve && match.Submit(slot, Command.ItemBuy(recipe.Id)).Ok)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (hero == null || hero.Items.Count >= LuneContent.ItemSlots)
|
if (hero == null || hero.Items.Count >= LuneContent.ItemSlots)
|
||||||
return;
|
return;
|
||||||
foreach (string itemId in profile.Items)
|
foreach (string itemId in profile.Items)
|
||||||
|
|
@ -148,14 +202,18 @@ namespace Luneraria.Core
|
||||||
string lane = WeakestLane(match, 1 - player.Team);
|
string lane = WeakestLane(match, 1 - player.Team);
|
||||||
match.Submit(slot, Command.BossLaneChoice(lane));
|
match.Submit(slot, Command.BossLaneChoice(lane));
|
||||||
}
|
}
|
||||||
if (!profile.HuntsBoss || match.RoundNumber < profile.BossFrom || !match.GrottoAvailable())
|
// S2 (extended rules): mid players also hunt the boss from round 8, so the Mondsegen of S1 really happens.
|
||||||
|
bool hunts = profile.HuntsBoss || (match.Extended && LuneMap.LaneIndex(player.Lane) == 1 && LaneSafe(match, player));
|
||||||
|
int from = profile.HuntsBoss ? profile.BossFrom : 8;
|
||||||
|
int troops = profile.HuntsBoss ? profile.BossTroops : 5;
|
||||||
|
if (!hunts || match.RoundNumber < from || !match.GrottoAvailable())
|
||||||
return;
|
return;
|
||||||
List<RosterEntry> roster = player.Draft.Roster;
|
List<RosterEntry> roster = player.Draft.Roster;
|
||||||
if (roster.Count < profile.BossTroops)
|
if (roster.Count < troops)
|
||||||
return;
|
return;
|
||||||
var picks = new List<RosterEntry>(roster);
|
var picks = new List<RosterEntry>(roster);
|
||||||
GdMath.SortStable(picks, (a, b) => a.Invested == b.Invested ? a.Seq < b.Seq : a.Invested > b.Invested);
|
GdMath.SortStable(picks, (a, b) => a.Invested == b.Invested ? a.Seq < b.Seq : a.Invested > b.Invested);
|
||||||
int keep = GdMath.Min(profile.BossTroops - 1, picks.Count);
|
int keep = GdMath.Min(troops - 1, picks.Count);
|
||||||
picks.RemoveRange(keep, picks.Count - keep);
|
picks.RemoveRange(keep, picks.Count - keep);
|
||||||
HeroState hero = player.Draft.Hero;
|
HeroState hero = player.Draft.Hero;
|
||||||
// The boss formula is public. Lanchester estimate: total HP x total damage of the
|
// The boss formula is public. Lanchester estimate: total HP x total damage of the
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,10 @@ namespace Luneraria.Core
|
||||||
public int GrottoActiveTeam = -1;
|
public int GrottoActiveTeam = -1;
|
||||||
/// <summary>none, success or failed.</summary>
|
/// <summary>none, success or failed.</summary>
|
||||||
public string GrottoOutcome = "none";
|
public string GrottoOutcome = "none";
|
||||||
|
/// <summary>S2 glyph per team: the held lane ("" none), when its towers stop being invulnerable, whether it fired.</summary>
|
||||||
|
public readonly string[] GlyphLane = { "", "" };
|
||||||
|
public readonly double[] GlyphUntil = { -1.0, -1.0 };
|
||||||
|
public readonly bool[] GlyphUsed = new bool[2];
|
||||||
|
|
||||||
int _nextId = 1;
|
int _nextId = 1;
|
||||||
readonly int[] _teamCount = new int[3];
|
readonly int[] _teamCount = new int[3];
|
||||||
|
|
@ -291,7 +295,7 @@ namespace Luneraria.Core
|
||||||
}
|
}
|
||||||
foreach (LuneUnit unit in living)
|
foreach (LuneUnit unit in living)
|
||||||
{
|
{
|
||||||
if (unit.IsMobile() || unit.Kind == "tower")
|
if (unit.IsMobile() || unit.Kind == "tower" || (unit.Kind == "core" && unit.Def.Damage > 0.0))
|
||||||
{
|
{
|
||||||
bool keep = unit.Target != null && unit.Target.Alive;
|
bool keep = unit.Target != null && unit.Target.Alive;
|
||||||
if (unit.LeapTarget != null)
|
if (unit.LeapTarget != null)
|
||||||
|
|
@ -350,10 +354,40 @@ namespace Luneraria.Core
|
||||||
RecordDeath(unit);
|
RecordDeath(unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TriggerGlyphs();
|
||||||
UpdateGrotto();
|
UpdateGrotto();
|
||||||
CheckEnd();
|
CheckEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>S2: a tower of the glyph lane is invulnerable while its team's glyph runs.</summary>
|
||||||
|
public bool Glyphed(LuneUnit unit)
|
||||||
|
{
|
||||||
|
if (unit.Kind != "tower" || unit.Team > 1)
|
||||||
|
return false;
|
||||||
|
return GlyphLane[unit.Team].Length > 0 && unit.Lane == GlyphLane[unit.Team] && Elapsed < GlyphUntil[unit.Team] - 0.000001;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>S2 (Dota 2 glyph): the first time a tower of the held lane is below the threshold, the glyph fires.</summary>
|
||||||
|
void TriggerGlyphs()
|
||||||
|
{
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
{
|
||||||
|
if (GlyphLane[team].Length == 0 || GlyphUsed[team])
|
||||||
|
continue;
|
||||||
|
IReadOnlyList<LuneUnit> buildings = Buildings.Values;
|
||||||
|
for (int i = 0; i < buildings.Count; i++)
|
||||||
|
{
|
||||||
|
LuneUnit tower = buildings[i];
|
||||||
|
if (tower.Alive && tower.Kind == "tower" && tower.Team == team && tower.Lane == GlyphLane[team] && tower.Hp < tower.MaxHp * Balance.GlyphThreshold)
|
||||||
|
{
|
||||||
|
GlyphUsed[team] = true;
|
||||||
|
GlyphUntil[team] = Elapsed + Balance.GlyphSeconds;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Expeditions fight each other first; the boss wakes when at most one team remains.</summary>
|
/// <summary>Expeditions fight each other first; the boss wakes when at most one team remains.</summary>
|
||||||
void UpdateGrotto()
|
void UpdateGrotto()
|
||||||
{
|
{
|
||||||
|
|
@ -463,11 +497,11 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
if (!current.IsBuilding() || BuildingAttackable(current, gates))
|
if (!current.IsBuilding() || BuildingAttackable(current, gates))
|
||||||
{
|
{
|
||||||
if (unit.Kind != "tower" || unit.Position.DistanceTo(current.Position) <= unit.AttackRange())
|
if (!unit.IsBuilding() || unit.Position.DistanceTo(current.Position) <= unit.AttackRange())
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
double radius = unit.Kind == "tower" ? unit.AttackRange() : Balance.AggroRadius;
|
double radius = unit.IsBuilding() ? unit.AttackRange() : Balance.AggroRadius;
|
||||||
LuneUnit best = null;
|
LuneUnit best = null;
|
||||||
double bestDistance = double.PositiveInfinity;
|
double bestDistance = double.PositiveInfinity;
|
||||||
string priority = unit.Def.Priority;
|
string priority = unit.Def.Priority;
|
||||||
|
|
@ -505,7 +539,7 @@ namespace Luneraria.Core
|
||||||
bestDistance = d;
|
bestDistance = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (best != null || unit.Kind == "tower")
|
if (best != null || unit.IsBuilding())
|
||||||
return best;
|
return best;
|
||||||
// Buildings: nearby attackable ones; at the end of a lane the whole base is in reach.
|
// Buildings: nearby attackable ones; at the end of a lane the whole base is in reach.
|
||||||
for (int i = 0; i < buildings.Count; i++)
|
for (int i = 0; i < buildings.Count; i++)
|
||||||
|
|
@ -647,7 +681,7 @@ namespace Luneraria.Core
|
||||||
{
|
{
|
||||||
unit.InRange = false;
|
unit.InRange = false;
|
||||||
LuneUnit target = unit.Target;
|
LuneUnit target = unit.Target;
|
||||||
if (target == null || !target.Alive || unit.Kind == "core")
|
if (target == null || !target.Alive || (unit.Kind == "core" && unit.Def.Damage <= 0.0))
|
||||||
{
|
{
|
||||||
unit.AttackProgress = 0.0;
|
unit.AttackProgress = 0.0;
|
||||||
return;
|
return;
|
||||||
|
|
@ -664,7 +698,7 @@ namespace Luneraria.Core
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unit.InRange = true;
|
unit.InRange = true;
|
||||||
double interval = unit.Kind == "tower" ? Balance.TowerAttackInterval : 1.0;
|
double interval = unit.IsBuilding() ? Balance.TowerAttackInterval : 1.0;
|
||||||
unit.AttackProgress += dt * unit.AttackSpeedFactor() / interval;
|
unit.AttackProgress += dt * unit.AttackSpeedFactor() / interval;
|
||||||
while (unit.AttackProgress >= 1.0 - 0.000001)
|
while (unit.AttackProgress >= 1.0 - 0.000001)
|
||||||
{
|
{
|
||||||
|
|
@ -704,6 +738,8 @@ namespace Luneraria.Core
|
||||||
// The building gate applies to every source: ranged, splash, DoT and abilities.
|
// The building gate applies to every source: ranged, splash, DoT and abilities.
|
||||||
if (target.IsBuilding() && !BuildingAttackable(target, gates))
|
if (target.IsBuilding() && !BuildingAttackable(target, gates))
|
||||||
return;
|
return;
|
||||||
|
if (Glyphed(target))
|
||||||
|
return;
|
||||||
double current;
|
double current;
|
||||||
_pendingDamage.TryGetValue(target.Id, out current);
|
_pendingDamage.TryGetValue(target.Id, out current);
|
||||||
_pendingDamage[target.Id] = current + amount;
|
_pendingDamage[target.Id] = current + amount;
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ namespace Luneraria.Core
|
||||||
SeedValue = options.Seed;
|
SeedValue = options.Seed;
|
||||||
Extended = options.Extended;
|
Extended = options.Extended;
|
||||||
Boss.Blessing = new int[2];
|
Boss.Blessing = new int[2];
|
||||||
|
Boss.GlyphReady = new[] { 1, 1 };
|
||||||
|
Boss.CoreLevel = new int[2];
|
||||||
RoundNumber = 1;
|
RoundNumber = 1;
|
||||||
Phase = Phase.Planning;
|
Phase = Phase.Planning;
|
||||||
Winner = Ongoing;
|
Winner = Ongoing;
|
||||||
|
|
@ -255,6 +257,8 @@ namespace Luneraria.Core
|
||||||
ItemSpec item;
|
ItemSpec item;
|
||||||
if (!ContentData.Items.TryGetValue(itemId, out item))
|
if (!ContentData.Items.TryGetValue(itemId, out item))
|
||||||
return Result(false, "Unbekanntes Item.");
|
return Result(false, "Unbekanntes Item.");
|
||||||
|
if (item.IsRecipe)
|
||||||
|
return Combine(hero, draft, item);
|
||||||
if (hero.Items.Contains(itemId))
|
if (hero.Items.Contains(itemId))
|
||||||
return Result(false, item.Name + " trägt dein Held bereits; Items sind einzigartig.");
|
return Result(false, item.Name + " trägt dein Held bereits; Items sind einzigartig.");
|
||||||
if (hero.Items.Count >= LuneContent.ItemSlots)
|
if (hero.Items.Count >= LuneContent.ItemSlots)
|
||||||
|
|
@ -320,6 +324,32 @@ 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 "glyph":
|
||||||
|
{
|
||||||
|
if (!Extended)
|
||||||
|
return Result(false, "Die Glyphe gibt es nur mit erweiterten Regeln.");
|
||||||
|
string lane = command.Lane ?? "";
|
||||||
|
if (lane.Length > 0 && LuneMap.LaneIndex(lane) < 0)
|
||||||
|
return Result(false, "Unbekannte Lane.");
|
||||||
|
if (lane.Length > 0 && RoundNumber < Boss.GlyphReady[player.Team])
|
||||||
|
return Result(false, string.Format("Die Glyphe ist ab Runde {0} wieder bereit.", Boss.GlyphReady[player.Team]));
|
||||||
|
draft.GlyphLane = lane;
|
||||||
|
return Result(true, lane.Length == 0 ? "Glyphe zurückgenommen." : "Glyphe für " + LuneMap.LaneNames[lane] + " bereitgehalten.");
|
||||||
|
}
|
||||||
|
case "core_upgrade":
|
||||||
|
{
|
||||||
|
if (!Extended)
|
||||||
|
return Result(false, "Ancient-Stufen gibt es nur mit erweiterten Regeln.");
|
||||||
|
int level = Boss.CoreLevel[player.Team] + draft.CoreInvest;
|
||||||
|
if (level >= Balance.CoreLevelMax)
|
||||||
|
return Result(false, "Der Ancient hat die höchste Stufe.");
|
||||||
|
int cost = Balance.CoreUpgradeCost(level);
|
||||||
|
if (draft.Gold < cost)
|
||||||
|
return Result(false, string.Format("Es fehlen {0} Gold.", cost - draft.Gold));
|
||||||
|
draft.Gold -= cost;
|
||||||
|
draft.CoreInvest += 1;
|
||||||
|
return Result(true, string.Format("Ancient auf Stufe {0} gestärkt ({1} Gold).", level + 1, cost));
|
||||||
|
}
|
||||||
case "buyback":
|
case "buyback":
|
||||||
{
|
{
|
||||||
if (!Extended)
|
if (!Extended)
|
||||||
|
|
@ -459,11 +489,27 @@ namespace Luneraria.Core
|
||||||
claim.Lane = draft.BossLane;
|
claim.Lane = draft.BossLane;
|
||||||
player.Undo.Clear();
|
player.Undo.Clear();
|
||||||
}
|
}
|
||||||
|
if (Extended)
|
||||||
|
CommitTeamActions();
|
||||||
Combat = new LuneCombat(Map, Balance, RoundNumber);
|
Combat = new LuneCombat(Map, Balance, RoundNumber);
|
||||||
foreach (BuildingDef definition in Map.Buildings)
|
foreach (BuildingDef definition in Map.Buildings)
|
||||||
{
|
{
|
||||||
if (BuildingHp[definition.Id] > 0.0)
|
if (BuildingHp[definition.Id] > 0.0)
|
||||||
Combat.AddBuilding(definition, BuildingHp[definition.Id]);
|
{
|
||||||
|
LuneUnit building = Combat.AddBuilding(definition, BuildingHp[definition.Id]);
|
||||||
|
int level = Extended && definition.Tier == "core" ? Boss.CoreLevel[definition.Team] : 0;
|
||||||
|
if (level > 0)
|
||||||
|
{
|
||||||
|
// S2 (Legion TD 2 king): the Ancient shoots at attackers in its reach.
|
||||||
|
building.Def.Damage = Balance.CoreDamage[GdMath.Min(level, Balance.CoreDamage.Length - 1)];
|
||||||
|
building.Def.AttackRange = Balance.CoreRange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Extended)
|
||||||
|
{
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
Combat.GlyphLane[team] = _glyphLane[team];
|
||||||
}
|
}
|
||||||
SpawnWaves();
|
SpawnWaves();
|
||||||
SpawnChampions();
|
SpawnChampions();
|
||||||
|
|
@ -530,6 +576,70 @@ namespace Luneraria.Core
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------- S2 extended rules
|
||||||
|
readonly string[] _glyphLane = { "", "" };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Team actions of the drafts (S2): the glyph lane of the first player in slot order who
|
||||||
|
/// holds one, and the Ancient levels all players of a team bought (capped; the excess is
|
||||||
|
/// refunded). Each level repairs the Ancient by CoreRepair.
|
||||||
|
/// </summary>
|
||||||
|
void CommitTeamActions()
|
||||||
|
{
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
{
|
||||||
|
_glyphLane[team] = "";
|
||||||
|
for (int slot = team * 3; slot < team * 3 + 3; slot++)
|
||||||
|
{
|
||||||
|
Player player = Players[slot];
|
||||||
|
if (_glyphLane[team].Length == 0 && player.Draft.GlyphLane.Length > 0 && RoundNumber >= Boss.GlyphReady[team])
|
||||||
|
_glyphLane[team] = player.Draft.GlyphLane;
|
||||||
|
for (int i = 0; i < player.Draft.CoreInvest; i++)
|
||||||
|
{
|
||||||
|
int level = Boss.CoreLevel[team];
|
||||||
|
if (level >= Balance.CoreLevelMax)
|
||||||
|
{
|
||||||
|
player.Gold += Balance.CoreUpgradeCost(level - 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Boss.CoreLevel[team] = level + 1;
|
||||||
|
foreach (BuildingDef definition in Map.Buildings)
|
||||||
|
{
|
||||||
|
if (definition.Team == team && definition.Tier == "core" && BuildingHp[definition.Id] > 0.0)
|
||||||
|
BuildingHp[definition.Id] = GdMath.Min(definition.MaxHp, BuildingHp[definition.Id] + Balance.CoreRepair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>S2 recipe (Dota 2 / League of Legends): both parts and the fee become the recipe item, freeing a slot.</summary>
|
||||||
|
CommandResult Combine(HeroState hero, Draft draft, ItemSpec item)
|
||||||
|
{
|
||||||
|
if (!Extended)
|
||||||
|
return Result(false, "Rezepte gibt es nur mit erweiterten Regeln.");
|
||||||
|
if (hero.Items.Contains(item.Id))
|
||||||
|
return Result(false, item.Name + " trägt dein Held bereits; Items sind einzigartig.");
|
||||||
|
foreach (string part in item.Parts)
|
||||||
|
{
|
||||||
|
if (!hero.Items.Contains(part))
|
||||||
|
return Result(false, item.Name + " braucht " + string.Join(" und ", Array.ConvertAll(item.Parts, p => ContentData.Items[p].Name)) + ".");
|
||||||
|
}
|
||||||
|
if (LuneContent.LevelFor(hero.Xp) < item.MinLevel)
|
||||||
|
return Result(false, string.Format("{0} erfordert Stufe {1}.", item.Name, item.MinLevel));
|
||||||
|
if (draft.Gold < item.RecipeFee)
|
||||||
|
return Result(false, string.Format("Es fehlen {0} Gold.", item.RecipeFee - draft.Gold));
|
||||||
|
draft.Gold -= item.RecipeFee;
|
||||||
|
int at = hero.Items.IndexOf(item.Parts[0]);
|
||||||
|
foreach (string part in item.Parts)
|
||||||
|
{
|
||||||
|
hero.Items.Remove(part);
|
||||||
|
hero.FreshItems.Remove(part);
|
||||||
|
}
|
||||||
|
hero.Items.Insert(GdMath.Min(at, hero.Items.Count), item.Id);
|
||||||
|
return Result(true, item.Name + " geschmiedet.");
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------- S1 extended rules
|
// ---------------------------------------------------------------- S1 extended rules
|
||||||
/// <summary>Legion TD 2: income a booked champion adds for good.</summary>
|
/// <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 ChampionIncome(int cost) { return cost / GdMath.Max(1, Balance.ChampionIncomeDivisor); }
|
||||||
|
|
@ -810,6 +920,12 @@ namespace Luneraria.Core
|
||||||
}
|
}
|
||||||
if (Extended)
|
if (Extended)
|
||||||
{
|
{
|
||||||
|
// S2 (Dota 2 glyph): only a glyph that fired goes on cooldown.
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
{
|
||||||
|
if (Combat.GlyphUsed[team])
|
||||||
|
Boss.GlyphReady[team] = RoundNumber + 1 + Balance.GlyphCooldown;
|
||||||
|
}
|
||||||
// S1 (Dota 2 buyback, adapted): a hero that fell is exhausted for the next combat.
|
// S1 (Dota 2 buyback, adapted): a hero that fell is exhausted for the next combat.
|
||||||
foreach (LuneUnit unit in Combat.Units)
|
foreach (LuneUnit unit in Combat.Units)
|
||||||
{
|
{
|
||||||
|
|
@ -894,6 +1010,7 @@ namespace Luneraria.Core
|
||||||
// S1 (League of Legends dragon souls): each conquest blesses the team for good.
|
// S1 (League of Legends dragon souls): each conquest blesses the team for good.
|
||||||
if (Extended)
|
if (Extended)
|
||||||
Boss.Blessing[team] = GdMath.Min(Balance.BlessingMax, Boss.Blessing[team] + 1);
|
Boss.Blessing[team] = GdMath.Min(Balance.BlessingMax, Boss.Blessing[team] + 1);
|
||||||
|
// (S2 glyph cooldowns are settled in Settle.)
|
||||||
result = "won";
|
result = "won";
|
||||||
}
|
}
|
||||||
else if (Combat.GrottoOutcome == "failed")
|
else if (Combat.GrottoOutcome == "failed")
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,14 @@ namespace Luneraria.Core
|
||||||
public List<Booking> NewBookings = new List<Booking>();
|
public List<Booking> NewBookings = new List<Booking>();
|
||||||
/// <summary>Lane chosen by the boss chooser in this planning, "" if unchanged.</summary>
|
/// <summary>Lane chosen by the boss chooser in this planning, "" if unchanged.</summary>
|
||||||
public string BossLane = "";
|
public string BossLane = "";
|
||||||
|
/// <summary>S2: lane the player holds the team glyph ready for, "" = none.</summary>
|
||||||
|
public string GlyphLane = "";
|
||||||
|
/// <summary>S2: Ancient levels this player buys in this planning.</summary>
|
||||||
|
public int CoreInvest;
|
||||||
|
|
||||||
public Draft Clone()
|
public Draft Clone()
|
||||||
{
|
{
|
||||||
var copy = new Draft { Gold = Gold, NextSeq = NextSeq, BossLane = BossLane, Hero = Hero != null ? Hero.Clone() : null };
|
var copy = new Draft { Gold = Gold, NextSeq = NextSeq, BossLane = BossLane, GlyphLane = GlyphLane, CoreInvest = CoreInvest, Hero = Hero != null ? Hero.Clone() : null };
|
||||||
foreach (RosterEntry entry in Roster)
|
foreach (RosterEntry entry in Roster)
|
||||||
copy.Roster.Add(entry.Clone());
|
copy.Roster.Add(entry.Clone());
|
||||||
foreach (Booking booking in NewBookings)
|
foreach (Booking booking in NewBookings)
|
||||||
|
|
@ -130,6 +134,10 @@ namespace Luneraria.Core
|
||||||
public int SettledRound;
|
public int SettledRound;
|
||||||
/// <summary>S1 (extended rules): Mondsegen stacks per team from grotto victories (League of Legends dragon souls).</summary>
|
/// <summary>S1 (extended rules): Mondsegen stacks per team from grotto victories (League of Legends dragon souls).</summary>
|
||||||
public int[] Blessing = new int[2];
|
public int[] Blessing = new int[2];
|
||||||
|
/// <summary>S2 (extended rules): first round each team's glyph can be held again (Dota 2).</summary>
|
||||||
|
public int[] GlyphReady = { 1, 1 };
|
||||||
|
/// <summary>S2 (extended rules): Ancient level per team, 0..3 (Legion TD 2 king upgrades).</summary>
|
||||||
|
public int[] CoreLevel = new int[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class BossLogEntry
|
public sealed class BossLogEntry
|
||||||
|
|
@ -254,5 +262,9 @@ namespace Luneraria.Core
|
||||||
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>
|
/// <summary>S1: buys an exhausted hero back to full strength for the next combat.</summary>
|
||||||
public static Command Buyback() { return new Command { Type = "buyback" }; }
|
public static Command Buyback() { return new Command { Type = "buyback" }; }
|
||||||
|
/// <summary>S2: hold the team glyph for a lane in the coming combat ("" withdraws it).</summary>
|
||||||
|
public static Command Glyph(string lane) { return new Command { Type = "glyph", Lane = lane }; }
|
||||||
|
/// <summary>S2: buy the next Ancient level for the team.</summary>
|
||||||
|
public static Command CoreUpgrade() { return new Command { Type = "core_upgrade" }; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,6 +226,11 @@ namespace Luneraria.Net
|
||||||
}
|
}
|
||||||
Int(text, boss.Blessing != null && boss.Blessing.Length > 0 ? boss.Blessing[0] : 0);
|
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);
|
Int(text, boss.Blessing != null && boss.Blessing.Length > 1 ? boss.Blessing[1] : 0);
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
{
|
||||||
|
Int(text, boss.GlyphReady != null && boss.GlyphReady.Length > team ? boss.GlyphReady[team] : 1);
|
||||||
|
Int(text, boss.CoreLevel != null && boss.CoreLevel.Length > team ? boss.CoreLevel[team] : 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)
|
||||||
|
|
@ -278,6 +283,8 @@ namespace Luneraria.Net
|
||||||
Int(text, draft.Gold);
|
Int(text, draft.Gold);
|
||||||
Int(text, draft.NextSeq);
|
Int(text, draft.NextSeq);
|
||||||
Str(text, draft.BossLane ?? "");
|
Str(text, draft.BossLane ?? "");
|
||||||
|
Str(text, draft.GlyphLane ?? "");
|
||||||
|
Int(text, draft.CoreInvest);
|
||||||
WriteRosterList(text, draft.Roster);
|
WriteRosterList(text, draft.Roster);
|
||||||
Int(text, draft.Hero != null ? 1 : 0);
|
Int(text, draft.Hero != null ? 1 : 0);
|
||||||
if (draft.Hero != null)
|
if (draft.Hero != null)
|
||||||
|
|
@ -657,6 +664,11 @@ namespace Luneraria.Net
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
state.Boss.Blessing = new[] { Integer(), Integer() };
|
state.Boss.Blessing = new[] { Integer(), Integer() };
|
||||||
|
for (int team = 0; team < 2; team++)
|
||||||
|
{
|
||||||
|
state.Boss.GlyphReady[team] = Integer(1);
|
||||||
|
state.Boss.CoreLevel[team] = 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());
|
||||||
|
|
@ -705,7 +717,7 @@ namespace Luneraria.Net
|
||||||
row.Bookings = ReadBookingList();
|
row.Bookings = ReadBookingList();
|
||||||
if (Flag())
|
if (Flag())
|
||||||
{
|
{
|
||||||
var draft = new Draft { Gold = Integer(), NextSeq = Integer(), BossLane = Text() ?? "" };
|
var draft = new Draft { Gold = Integer(), NextSeq = Integer(), BossLane = Text() ?? "", GlyphLane = Text() ?? "", CoreInvest = Integer() };
|
||||||
draft.Roster = ReadRosterList();
|
draft.Roster = ReadRosterList();
|
||||||
if (Flag())
|
if (Flag())
|
||||||
draft.Hero = ReadHero();
|
draft.Hero = ReadHero();
|
||||||
|
|
|
||||||
|
|
@ -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(), new S1RulesSuite() };
|
return new List<CheckSuite> { new ContentSuite(), new CoreRulesSuite(), new M2RulesSuite(), new GoldenSuite(), new DeterminismSuite(), new NetSuite(), new NetRoundsSuite(), new H1HeroSuite(), new S1RulesSuite(), new S2RulesSuite() };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,10 @@ namespace Luneraria.Core.Tests
|
||||||
|
|
||||||
void CompareItems(OrderedMap<string, object> expected)
|
void CompareItems(OrderedMap<string, object> expected)
|
||||||
{
|
{
|
||||||
Check(expected.Keys.SequenceEqual(ContentData.Items.Keys), "items: same ids in the same order");
|
// S2 recipe items follow the reference items; those must match exactly.
|
||||||
|
Check(expected.Keys.SequenceEqual(ContentData.ReferenceItems) && ContentData.Items.Keys.Take(ContentData.ReferenceItems.Length).SequenceEqual(ContentData.ReferenceItems),
|
||||||
|
"items: same reference ids in the same order, recipes after them");
|
||||||
|
Check(ContentData.Items.Values.Skip(ContentData.ReferenceItems.Length).All(item => item.IsRecipe), "items after the reference are recipes (S2)");
|
||||||
foreach (KeyValuePair<string, object> pair in expected)
|
foreach (KeyValuePair<string, object> pair in expected)
|
||||||
{
|
{
|
||||||
var entry = MiniJson.Obj(pair.Value);
|
var entry = MiniJson.Obj(pair.Value);
|
||||||
|
|
|
||||||
162
Assets/Luneraria/Tests/Core/S2RulesSuite.cs
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Luneraria.Core.Tests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// S2: glyph (Dota 2), item recipes (Dota 2 / League of Legends), Ancient levels (Legion TD 2
|
||||||
|
/// king) and the bots' grotto hunt under the extended rules; none of it under the reference rules.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class S2RulesSuite : CheckSuite
|
||||||
|
{
|
||||||
|
public override string Name { get { return "s2"; } }
|
||||||
|
|
||||||
|
protected override void RunChecks()
|
||||||
|
{
|
||||||
|
TestReference();
|
||||||
|
TestGlyph();
|
||||||
|
TestRecipes();
|
||||||
|
TestCore();
|
||||||
|
TestBots();
|
||||||
|
}
|
||||||
|
|
||||||
|
static LuneMatch NewMatch(bool extended, int seed = 51)
|
||||||
|
{
|
||||||
|
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 TestReference()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(false);
|
||||||
|
state.Players[0].Draft.Gold += 3000;
|
||||||
|
Check(!state.Submit(0, Command.Glyph("mid")).Ok, "reference: no glyph");
|
||||||
|
Check(!state.Submit(0, Command.CoreUpgrade()).Ok, "reference: no Ancient levels");
|
||||||
|
state.Submit(0, Command.HeroBuy("held_ringborke"));
|
||||||
|
state.Submit(0, Command.ItemBuy("basaltschild"));
|
||||||
|
state.Submit(0, Command.ItemBuy("rankenmantel"));
|
||||||
|
Check(!state.Submit(0, Command.ItemBuy("bollwerk")).Ok, "reference: no recipes");
|
||||||
|
state.BeginCombat();
|
||||||
|
Check(state.Combat.Buildings.Values.All(b => b.Kind != "core" || b.Def.Damage == 0.0), "reference: the Ancients do not shoot");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGlyph()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
Check(!state.Submit(0, Command.Glyph("river")).Ok, "unknown lane rejected");
|
||||||
|
Check(state.Submit(1, Command.Glyph("mid")).Ok && state.Players[1].Draft.GlyphLane == "mid", "a player holds the team glyph for a lane");
|
||||||
|
state.Submit(0, Command.Glyph("top"));
|
||||||
|
state.BeginCombat();
|
||||||
|
Check(state.Combat.GlyphLane[0] == "top", "the first seat in slot order decides the team glyph");
|
||||||
|
LuneUnit tower = state.Combat.Buildings["blau_top_outer"];
|
||||||
|
tower.Hp = tower.MaxHp * 0.4;
|
||||||
|
state.Combat.Step();
|
||||||
|
Check(state.Combat.GlyphUsed[0] && state.Combat.Glyphed(tower), "the glyph fires once a tower of the lane is below half health");
|
||||||
|
double before = tower.Hp;
|
||||||
|
var gates = state.Combat.AliveMap();
|
||||||
|
state.Combat.QueueDamage(tower, 500.0, gates);
|
||||||
|
Check(!state.Combat.HasPendingDamage(tower), "glyphed towers take no damage");
|
||||||
|
Check(!state.Combat.Glyphed(state.Combat.Buildings["blau_mid_outer"]), "other lanes are not protected");
|
||||||
|
for (int i = 0; i < 120; i++)
|
||||||
|
state.Combat.Step();
|
||||||
|
Check(!state.Combat.Glyphed(tower), "the glyph ends after 5 s");
|
||||||
|
state.FinishCombat();
|
||||||
|
Check(state.Boss.GlyphReady[0] == state.RoundNumber - 1 + 1 + 4, "a fired glyph returns after 4 rounds (" + state.Boss.GlyphReady[0] + ")");
|
||||||
|
Check(!state.Submit(0, Command.Glyph("top")).Ok, "on cooldown the glyph cannot be held");
|
||||||
|
Check(state.Boss.GlyphReady[1] == 1, "the other team's glyph is untouched");
|
||||||
|
LuneMatch idle = NewMatch(true);
|
||||||
|
idle.Submit(2, Command.Glyph("bot"));
|
||||||
|
idle.BeginCombat();
|
||||||
|
idle.FinishCombat();
|
||||||
|
Check(idle.Combat.GlyphUsed[0] || idle.Boss.GlyphReady[0] == 1, "a glyph that never fired costs no cooldown");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestRecipes()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
state.Players[0].Draft.Gold += 3000;
|
||||||
|
state.Submit(0, Command.HeroBuy("held_ringborke"));
|
||||||
|
Check(!state.Submit(0, Command.ItemBuy("bollwerk")).Ok, "a recipe needs its parts");
|
||||||
|
state.Submit(0, Command.ItemBuy("basaltschild"));
|
||||||
|
state.Submit(0, Command.ItemBuy("rankenmantel"));
|
||||||
|
state.Submit(0, Command.ItemBuy("glutklinge"));
|
||||||
|
int gold = state.GoldOf(0);
|
||||||
|
Check(state.Submit(0, Command.ItemBuy("bollwerk")).Ok && state.GoldOf(0) == gold - 120, "combining costs the fee of 120");
|
||||||
|
HeroState hero = state.Players[0].Draft.Hero;
|
||||||
|
Check(hero.Items.SequenceEqual(new[] { "bollwerk", "glutklinge" }), "parts become the recipe item, a slot is free");
|
||||||
|
UnitDef definition = LuneContent.HeroDefinition(hero);
|
||||||
|
Check(GdMath.IsEqualApprox(definition.MaxHp, 2200.0 + 600.0) && GdMath.IsEqualApprox(definition.ReductionBonus, 0.15), "Bollwerk: +600 health, +15 % reduction");
|
||||||
|
Check(state.Undo(0).Ok && state.Players[0].Draft.Hero.Items.Count == 3, "combining can be undone");
|
||||||
|
state.Submit(0, Command.ItemBuy("bollwerk"));
|
||||||
|
Check(!state.Submit(0, Command.ItemBuy("splitterkrone")).Ok, "Splitterkrone needs its parts and level 5");
|
||||||
|
state.BeginCombat();
|
||||||
|
state.FinishCombat();
|
||||||
|
int sellGold = state.GoldOf(0);
|
||||||
|
Check(state.Submit(0, Command.ItemSell("bollwerk")).Ok && state.GoldOf(0) == sellGold + 250, "a recipe item sells for half its total (250)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestCore()
|
||||||
|
{
|
||||||
|
LuneMatch state = NewMatch(true);
|
||||||
|
state.Players[0].Draft.Gold += 3000;
|
||||||
|
state.Players[1].Draft.Gold += 3000;
|
||||||
|
state.BuildingHp["blau_ancient"] = 5000.0;
|
||||||
|
int gold = state.GoldOf(0);
|
||||||
|
Check(state.Submit(0, Command.CoreUpgrade()).Ok && state.GoldOf(0) == gold - 150, "the first Ancient level costs 150");
|
||||||
|
Check(state.Submit(0, Command.CoreUpgrade()).Ok && state.GoldOf(0) == gold - 450, "the second costs 300");
|
||||||
|
Check(state.Submit(1, Command.CoreUpgrade()).Ok && state.Submit(1, Command.CoreUpgrade()).Ok, "a teammate buys too (priced by the committed level)");
|
||||||
|
state.BeginCombat();
|
||||||
|
Check(state.Boss.CoreLevel[0] == 3 && state.Boss.CoreLevel[1] == 0, "levels are capped at 3 for the team");
|
||||||
|
Check(state.Players[1].Gold > 0, "the teammate's excess level is refunded");
|
||||||
|
LuneUnit core = state.Combat.Buildings["blau_ancient"];
|
||||||
|
Check(GdMath.IsEqualApprox(core.Def.Damage, 110.0) && GdMath.IsEqualApprox(core.Def.AttackRange, 7.5), "level 3: the Ancient shoots 110 in 7.5 m");
|
||||||
|
Check(GdMath.IsEqualApprox(core.Hp, 8000.0), "each level repaired 1,000 (5,000 → 8,000)");
|
||||||
|
Check(!state.Submit(0, Command.CoreUpgrade()).Ok, "no commands in combat");
|
||||||
|
state.FinishCombat();
|
||||||
|
Check(!state.Submit(0, Command.CoreUpgrade()).Ok, "no level above 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBots()
|
||||||
|
{
|
||||||
|
int hunts = 0;
|
||||||
|
int blessings = 0;
|
||||||
|
int combines = 0;
|
||||||
|
int glyphs = 0;
|
||||||
|
int cores = 0;
|
||||||
|
string first = "";
|
||||||
|
for (int seed = 1; seed <= 12; seed++)
|
||||||
|
{
|
||||||
|
LuneMatch match = NewMatch(true, seed);
|
||||||
|
while (match.Phase != Phase.Over && match.RoundNumber <= 80)
|
||||||
|
{
|
||||||
|
for (int slot = 0; slot < 6; slot++)
|
||||||
|
LuneBot.Plan(match, slot);
|
||||||
|
if (match.Players.Any(p => p.Draft.Roster.Any(r => r.Lane == LuneMatch.Grotto)))
|
||||||
|
hunts++;
|
||||||
|
match.BeginCombat();
|
||||||
|
match.FinishCombat();
|
||||||
|
glyphs += match.Combat.GlyphUsed.Count(used => used);
|
||||||
|
}
|
||||||
|
blessings += match.Boss.Blessing[0] + match.Boss.Blessing[1];
|
||||||
|
cores += match.Boss.CoreLevel[0] + match.Boss.CoreLevel[1];
|
||||||
|
combines += match.Players.Count(p => p.Hero != null && p.Hero.Items.Any(id => ContentData.Items[id].IsRecipe));
|
||||||
|
if (seed == 1)
|
||||||
|
first = match.RoundNumber + "/" + match.Winner;
|
||||||
|
}
|
||||||
|
Check(hunts > 0 && blessings > 0, "extended bots go to the grotto and win Mondsegen (" + hunts + " expeditions, " + blessings + " stacks in 12 matches)");
|
||||||
|
// Bots recruit heroes late (reference bot behaviour), so recipes rarely come up in whole
|
||||||
|
// matches; a prepared planning shows that a bot forges one when it can.
|
||||||
|
LuneMatch forge = NewMatch(true, 3);
|
||||||
|
Player bot = forge.Players[0];
|
||||||
|
bot.Hero = new HeroState { Uid = "h0-9", Seq = 9, HeroId = "held_ringborke", Lane = bot.Lane, Xp = 300, Invested = 260, Items = new List<string> { "basaltschild", "rankenmantel" } };
|
||||||
|
bot.Gold = 1200;
|
||||||
|
bot.Draft = forge.Snapshot(bot);
|
||||||
|
LuneBot.Plan(forge, 0);
|
||||||
|
Check(bot.Draft.Hero.Items.Contains("bollwerk"), "a bot forges Bollwerk from its parts (" + string.Join("+", bot.Draft.Hero.Items) + ")");
|
||||||
|
Check(glyphs > 0, "extended bots fire glyphs (" + glyphs + ")");
|
||||||
|
Note("12 extended matches: expeditions " + hunts + ", Mondsegen " + blessings + ", recipe heroes " + combines + ", glyphs " + glyphs + ", Ancient levels " + cores + "; seed 1 " + first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Luneraria/Tests/Core/S2RulesSuite.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04183de557804f08aac74a3d73fe5f3c
|
||||||
|
|
@ -53,5 +53,6 @@ namespace Luneraria.Core.Tests
|
||||||
[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()); }
|
[Test] public void S1Rules() { RunSuite(new S1RulesSuite()); }
|
||||||
|
[Test] public void S2Rules() { RunSuite(new S2RulesSuite()); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,8 @@ namespace Luneraria.UI.Tests
|
||||||
// Items: buy one, the card turns unique; slots fill up; higher items need levels.
|
// Items: buy one, the card turns unique; slots fill up; higher items need levels.
|
||||||
yield return s.Tap(() => s.Hud.HeroDetail.Tabs.Tabs[LuneHeroDetail.TabItems]);
|
yield return s.Tap(() => s.Hud.HeroDetail.Tabs.Tabs[LuneHeroDetail.TabItems]);
|
||||||
Dictionary<string, LuneItemSlot> cards = s.Hud.HeroDetail.ItemCards;
|
Dictionary<string, LuneItemSlot> cards = s.Hud.HeroDetail.ItemCards;
|
||||||
Check(cards.Count == LuneContent.Items.Count, "item shop lists every item");
|
// S2: recipes only under the extended rules; the verify plays the reference rules.
|
||||||
|
Check(cards.Count == LuneContent.Items.Values.Count(item => !item.IsRecipe || s.Match.Extended), "item shop lists every item of the rules in play");
|
||||||
string first = cards.First(c => c.Value.Current == LuneItemSlot.State.Available).Key;
|
string first = cards.First(c => c.Value.Current == LuneItemSlot.State.Available).Key;
|
||||||
yield return s.Tap(() => cards[first]);
|
yield return s.Tap(() => cards[first]);
|
||||||
Check(s.Model.OwnHero.Items.Contains(first), "item bought by click");
|
Check(s.Model.OwnHero.Items.Contains(first), "item bought by click");
|
||||||
|
|
|
||||||
130
Assets/Luneraria/Tests/UI/PlayMode/S2RulesViewsTests.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
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>
|
||||||
|
/// S2 in the real scene: the team actions (glyph lane, Ancient level) in the team panel, the
|
||||||
|
/// recipe in the hero window's item shop, the glyph ring on an invulnerable tower and a
|
||||||
|
/// levelled Ancient in combat. S2Bilder (explicit) captures them at 1920×1080;
|
||||||
|
/// LUNE_EVIDENCE names the folder below docs/port/screenshots (default s2/spiel).
|
||||||
|
/// </summary>
|
||||||
|
public sealed class S2RulesViewsTests
|
||||||
|
{
|
||||||
|
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 Plan(HudSession s)
|
||||||
|
{
|
||||||
|
Assert.IsTrue(s.Match.Extended, "extended match");
|
||||||
|
yield return s.Starter();
|
||||||
|
s.Leave();
|
||||||
|
Send(s, Command.HeroBuy("held_ringborke"));
|
||||||
|
Send(s, Command.ItemBuy("basaltschild"));
|
||||||
|
Send(s, Command.ItemBuy("rankenmantel"));
|
||||||
|
// Public fixture: Blau's outer mid tower is already damaged.
|
||||||
|
s.Match.BuildingHp["blau_mid_outer"] = s.Match.Map.BuildingDef("blau_mid_outer").MaxHp * 0.55;
|
||||||
|
s.Sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
static GameObject Ring(string id)
|
||||||
|
{
|
||||||
|
return UnityEngine.Object.FindObjectsByType<Transform>(FindObjectsInactive.Include, FindObjectsSortMode.None)
|
||||||
|
.Where(t => t.name == "GlyphRing_" + id).Select(t => t.gameObject).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest, Timeout(600000)]
|
||||||
|
public IEnumerator S2Hud()
|
||||||
|
{
|
||||||
|
var s = new HudSession();
|
||||||
|
yield return s.Open(1920, 1080, seed: 4, startRound: 6, extraGold: 3000, extended: true);
|
||||||
|
yield return Plan(s);
|
||||||
|
Assert.IsTrue(HudSession.Visible(s.Hud.Team.CoreButton), "team actions are visible under the extended rules");
|
||||||
|
s.Click(s.Hud.Team.GlyphButtons["mid"]);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
Assert.AreEqual("mid", s.Me.Draft.GlyphLane, "the mid button holds the glyph");
|
||||||
|
Assert.IsTrue(s.Hud.Team.GlyphButtons["mid"].On, "the button shows it");
|
||||||
|
int gold = s.Me.Draft.Gold;
|
||||||
|
s.Click(s.Hud.Team.CoreButton);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
Assert.AreEqual(1, s.Me.Draft.CoreInvest, "the Ancient button buys a level");
|
||||||
|
Assert.AreEqual(gold - 150, s.Me.Draft.Gold, "for 150 gold");
|
||||||
|
s.Hud.OpenHero(LuneHeroDetail.TabItems);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
Assert.IsTrue(s.Hud.HeroDetail.ItemCards.ContainsKey("bollwerk"), "recipes appear in the shop");
|
||||||
|
Assert.AreEqual(LuneItemSlot.State.Available, s.Hud.HeroDetail.ItemCards["bollwerk"].Current, "Bollwerk can be forged");
|
||||||
|
Assert.AreEqual(LuneItemSlot.State.MissingParts, s.Hud.HeroDetail.ItemCards["glutsturm"].Current, "Glutsturm lacks its parts");
|
||||||
|
s.Click(s.Hud.HeroDetail.ItemCards["bollwerk"]);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
s.Sync();
|
||||||
|
CollectionAssert.AreEqual(new[] { "bollwerk" }, s.Me.Draft.Hero.Items, "the shop card forges Bollwerk");
|
||||||
|
s.Hud.CloseModal();
|
||||||
|
s.Game.Key(KeyCode.Space);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
Assert.AreEqual("mid", s.Match.Combat.GlyphLane[0], "Blau holds the glyph on mid");
|
||||||
|
LuneUnit core = s.Match.Combat.Buildings["blau_ancient"];
|
||||||
|
Assert.AreEqual(45.0, core.Def.Damage, 1e-9, "the Ancient shoots at level 1");
|
||||||
|
LuneUnit tower = s.Match.Combat.Buildings["blau_mid_outer"];
|
||||||
|
tower.Hp = tower.MaxHp * .45;
|
||||||
|
s.Runner.Advance(.05);
|
||||||
|
yield return HudSession.Frames(3);
|
||||||
|
Assert.IsTrue(s.Match.Combat.Glyphed(tower), "the glyph fires below half health");
|
||||||
|
GameObject ring = Ring("blau_mid_outer");
|
||||||
|
Assert.IsTrue(ring != null && ring.activeSelf, "the tower shows the glyph ring");
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnityTest, Timeout(600000), Explicit("Bildwerkzeug: nur gezielt per -testFilter")]
|
||||||
|
public IEnumerator S2Bilder()
|
||||||
|
{
|
||||||
|
string evidence = Environment.GetEnvironmentVariable("LUNE_EVIDENCE");
|
||||||
|
string folder = "docs/port/screenshots/" + (string.IsNullOrEmpty(evidence) ? "s2/spiel" : evidence) + "/";
|
||||||
|
var s = new HudSession();
|
||||||
|
yield return s.Open(1920, 1080, seed: 4, startRound: 6, extraGold: 3000, extended: true);
|
||||||
|
yield return Plan(s);
|
||||||
|
Send(s, Command.Glyph("mid"));
|
||||||
|
Send(s, Command.CoreUpgrade());
|
||||||
|
yield return new WaitForSecondsRealtime(.5f);
|
||||||
|
yield return s.Shot(folder + "01-team-aktionen.png");
|
||||||
|
s.Hover(s.Hud.Team.CoreButton);
|
||||||
|
yield return new WaitForSecondsRealtime(.3f);
|
||||||
|
yield return s.Shot(folder + "02-ancient-tooltip.png");
|
||||||
|
s.Leave();
|
||||||
|
s.Hud.OpenHero(LuneHeroDetail.TabItems);
|
||||||
|
yield return new WaitForSecondsRealtime(.6f);
|
||||||
|
s.Hover(s.Hud.HeroDetail.ItemCards["bollwerk"]);
|
||||||
|
yield return new WaitForSecondsRealtime(.3f);
|
||||||
|
yield return s.Shot(folder + "03-rezept-im-shop.png");
|
||||||
|
Send(s, Command.ItemBuy("bollwerk"));
|
||||||
|
s.Leave();
|
||||||
|
yield return new WaitForSecondsRealtime(.4f);
|
||||||
|
yield return s.Shot(folder + "04-bollwerk-geschmiedet.png");
|
||||||
|
s.Hud.CloseModal();
|
||||||
|
s.Game.Key(KeyCode.Space);
|
||||||
|
yield return HudSession.Frames(2);
|
||||||
|
LuneUnit tower = s.Match.Combat.Buildings["blau_mid_outer"];
|
||||||
|
tower.Hp = tower.MaxHp * .45;
|
||||||
|
s.Runner.Advance(.05);
|
||||||
|
yield return s.Fight(1.0);
|
||||||
|
s.Driver.Paused = true;
|
||||||
|
s.Rig.LookAt(tower.Position, 14f, true);
|
||||||
|
yield return new WaitForSecondsRealtime(.5f);
|
||||||
|
yield return s.Shot(folder + "05-glyphe-am-turm.png");
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f22ed633e8804b2f85703375622e0e99
|
||||||
|
|
@ -115,6 +115,14 @@ namespace Luneraria.UI
|
||||||
|
|
||||||
readonly List<Row> _rows = new List<Row>();
|
readonly List<Row> _rows = new List<Row>();
|
||||||
readonly Label _header;
|
readonly Label _header;
|
||||||
|
/// <summary>S2 team actions (extended rules): glyph lane toggles and the Ancient level.</summary>
|
||||||
|
public event Action<Command> CommandRequested;
|
||||||
|
public readonly Dictionary<string, LuneButton> GlyphButtons = new Dictionary<string, LuneButton>();
|
||||||
|
public LuneButton CoreButton;
|
||||||
|
VisualElement _actions;
|
||||||
|
Label _glyphState;
|
||||||
|
Label _coreState;
|
||||||
|
string _actionSignature = "";
|
||||||
readonly LuneGlyphElement _emblem;
|
readonly LuneGlyphElement _emblem;
|
||||||
string _signature = "";
|
string _signature = "";
|
||||||
|
|
||||||
|
|
@ -145,6 +153,74 @@ namespace Luneraria.UI
|
||||||
int captured = index;
|
int captured = index;
|
||||||
LuneTooltip.Attach(element, () => Tip(captured));
|
LuneTooltip.Attach(element, () => Tip(captured));
|
||||||
}
|
}
|
||||||
|
// S2: team actions; hidden under the reference rules.
|
||||||
|
_actions = LuneUi.Box(true, 3f, column);
|
||||||
|
_actions.style.marginTop = 4f;
|
||||||
|
VisualElement glyphRow = LuneUi.Box(false, 4f, _actions);
|
||||||
|
LuneUi.Glyph("tower", LuneUiTheme.BronzeLight, 14f, glyphRow);
|
||||||
|
LuneUi.Label("GLYPHE", "HudCaps", glyphRow).style.minWidth = 64f;
|
||||||
|
foreach (string lane in LuneMap.Lanes)
|
||||||
|
{
|
||||||
|
var button = new LuneButton(lane == "bot" ? "BOT" : LuneUi.Upper(LuneMap.LaneNames[lane]), LuneButtons.HudToggle, glyphRow);
|
||||||
|
string captured = lane;
|
||||||
|
button.Clicked += () => CommandRequested?.Invoke(Command.Glyph(button.On ? "" : captured));
|
||||||
|
GlyphButtons[lane] = button;
|
||||||
|
}
|
||||||
|
_glyphState = LuneUi.Label("", "HudMuted", glyphRow);
|
||||||
|
LuneTooltip.Attach(glyphRow, () => new LuneTip
|
||||||
|
{
|
||||||
|
Title = "Glyphe (wie in Dota 2)", Glyph = "tower", GlyphColor = LuneUiTheme.BronzeLight,
|
||||||
|
Text = "Halte die Glyphe für eine Lane bereit: Fällt dort im Kampf ein eigener Turm unter 50 %, werden die Türme der Lane 5 s unverwundbar. Gilt für das ganze Team; danach 4 Runden Abklingzeit. Löst sie nicht aus, bleibt sie bereit.",
|
||||||
|
Note = "Entscheidet sich ein Mitspieler auf einem früheren Platz anders, gilt seine Wahl.",
|
||||||
|
});
|
||||||
|
VisualElement coreRow = LuneUi.Box(false, 4f, _actions);
|
||||||
|
LuneUi.Glyph("crown", LuneUiTheme.BronzeLight, 14f, coreRow);
|
||||||
|
LuneUi.Label("ANCIENT", "HudCaps", coreRow).style.minWidth = 64f;
|
||||||
|
_coreState = LuneUi.Label("", "HudText", coreRow);
|
||||||
|
_coreState.style.flexGrow = 1f;
|
||||||
|
CoreButton = new LuneButton("", LuneButtons.HudButton, coreRow);
|
||||||
|
CoreButton.Clicked += () => CommandRequested?.Invoke(Command.CoreUpgrade());
|
||||||
|
LuneTooltip.Attach(coreRow, () => new LuneTip
|
||||||
|
{
|
||||||
|
Title = "Ancient stärken (wie der König in Legion TD 2)", Glyph = "crown", GlyphColor = LuneUiTheme.BronzeLight,
|
||||||
|
Text = "Jeder im Team kann Stufen kaufen (höchstens 3). Jede Stufe repariert den Ancient um 1.000 und lässt ihn auf Angreifer in 7,5 m schießen: 45 / 75 / 110 Schaden pro Sekunde.",
|
||||||
|
Note = "Preis: 150 × nächste Stufe.",
|
||||||
|
});
|
||||||
|
LuneUi.Show(_actions, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>S2: glyph and Ancient level of the own team (planning commands; hidden under the reference rules).</summary>
|
||||||
|
public void ApplyActions(LuneHudModel model)
|
||||||
|
{
|
||||||
|
LuneMatch state = model.State;
|
||||||
|
bool on = state.Extended;
|
||||||
|
int team = model.Team;
|
||||||
|
Draft draft = model.Me.Draft;
|
||||||
|
int ready = state.Boss.GlyphReady[team];
|
||||||
|
int level = state.Boss.CoreLevel[team];
|
||||||
|
int pending = model.Planning ? draft.CoreInvest : 0;
|
||||||
|
string held = model.Planning ? draft.GlyphLane : "";
|
||||||
|
string signature = on + "|" + model.Planning + "|" + model.Locked + "|" + ready + "|" + state.RoundNumber + "|" + level + "|" + pending + "|" + held + "|" + model.OwnGold;
|
||||||
|
if (signature == _actionSignature)
|
||||||
|
return;
|
||||||
|
_actionSignature = signature;
|
||||||
|
LuneUi.Show(_actions, on);
|
||||||
|
if (!on)
|
||||||
|
return;
|
||||||
|
bool canAct = model.Planning && !model.Locked;
|
||||||
|
bool glyphReady = state.RoundNumber >= ready;
|
||||||
|
foreach (KeyValuePair<string, LuneButton> pair in GlyphButtons)
|
||||||
|
{
|
||||||
|
pair.Value.On = held == pair.Key;
|
||||||
|
pair.Value.Disabled = !canAct || !glyphReady;
|
||||||
|
}
|
||||||
|
_glyphState.text = glyphReady ? (held.Length > 0 ? "bereit" : "") : "ab Runde " + ready;
|
||||||
|
int shown = level + pending;
|
||||||
|
_coreState.text = "STUFE " + shown + "/" + state.Balance.CoreLevelMax + (pending > 0 ? " (+" + pending + ")" : "");
|
||||||
|
bool max = shown >= state.Balance.CoreLevelMax;
|
||||||
|
int cost = max ? 0 : state.Balance.CoreUpgradeCost(shown);
|
||||||
|
CoreButton.Text = max ? "MAX" : "+" + cost + " G";
|
||||||
|
CoreButton.Disabled = !canAct || max || model.OwnGold < cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>S1: Mondsegen stacks of the own team (-1 under the reference rules).</summary>
|
/// <summary>S1: Mondsegen stacks of the own team (-1 under the reference rules).</summary>
|
||||||
|
|
@ -401,17 +477,19 @@ namespace Luneraria.UI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class LuneItemSlot : LuneButton
|
public sealed class LuneItemSlot : LuneButton
|
||||||
{
|
{
|
||||||
public enum State { Empty, Equipped, Available, Unaffordable, Incompatible, Unique, Full, Locked }
|
public enum State { Empty, Equipped, Available, Unaffordable, Incompatible, Unique, Full, Locked, MissingParts }
|
||||||
|
|
||||||
static readonly Dictionary<State, string> StateText = new Dictionary<State, string>
|
static readonly Dictionary<State, string> StateText = new Dictionary<State, string>
|
||||||
{
|
{
|
||||||
{ State.Equipped, "AUSGERÜSTET" }, { State.Available, "KAUFBAR" }, { State.Unaffordable, "ZU TEUER" }, { State.Incompatible, "STUFE FEHLT" },
|
{ State.Equipped, "AUSGERÜSTET" }, { State.Available, "KAUFBAR" }, { State.Unaffordable, "ZU TEUER" }, { State.Incompatible, "STUFE FEHLT" },
|
||||||
{ State.Unique, "GETRAGEN" }, { State.Full, "PLÄTZE VOLL" }, { State.Locked, "GESPERRT" }, { State.Empty, "LEER" },
|
{ State.Unique, "GETRAGEN" }, { State.Full, "PLÄTZE VOLL" }, { State.Locked, "GESPERRT" }, { State.Empty, "LEER" }, { State.MissingParts, "TEILE FEHLEN" },
|
||||||
};
|
};
|
||||||
static readonly Dictionary<string, Color> ItemColors = new Dictionary<string, Color>
|
static readonly Dictionary<string, Color> ItemColors = new Dictionary<string, Color>
|
||||||
{
|
{
|
||||||
{ "basaltschild", LuneUiTheme.Hex("9fb2c8") }, { "glutklinge", LuneUiTheme.Hex("ff9a5a") }, { "rankenmantel", LuneUiTheme.Hex("8fd08a") },
|
{ "basaltschild", LuneUiTheme.Hex("9fb2c8") }, { "glutklinge", LuneUiTheme.Hex("ff9a5a") }, { "rankenmantel", LuneUiTheme.Hex("8fd08a") },
|
||||||
{ "funkenfokus", LuneUiTheme.Hex("f5d76a") }, { "quellamulett", LuneUiTheme.Hex("7ad6e6") }, { "kristallsplitter", LuneUiTheme.Hex("c7a2ff") },
|
{ "funkenfokus", LuneUiTheme.Hex("f5d76a") }, { "quellamulett", LuneUiTheme.Hex("7ad6e6") }, { "kristallsplitter", LuneUiTheme.Hex("c7a2ff") },
|
||||||
|
// S2 recipes: the colour of their first part, a little brighter.
|
||||||
|
{ "bollwerk", LuneUiTheme.Hex("c3d3e6") }, { "glutsturm", LuneUiTheme.Hex("ffb27a") }, { "quellherz", LuneUiTheme.Hex("a2e8f2") }, { "splitterkrone", LuneUiTheme.Hex("dcc2ff") },
|
||||||
};
|
};
|
||||||
|
|
||||||
public string ItemId = "";
|
public string ItemId = "";
|
||||||
|
|
@ -485,13 +563,13 @@ namespace Luneraria.UI
|
||||||
}
|
}
|
||||||
LuneContent.Items.TryGetValue(ItemId, out ItemSpec item);
|
LuneContent.Items.TryGetValue(ItemId, out ItemSpec item);
|
||||||
_name.text = item != null ? item.Name : "";
|
_name.text = item != null ? item.Name : "";
|
||||||
_cost.text = item != null ? item.Cost.ToString() : "";
|
_cost.text = item == null ? "" : item.IsRecipe ? "+" + item.RecipeFee : item.Cost.ToString();
|
||||||
LuneUi.SetColor(_cost, state == State.Unaffordable ? LuneUiTheme.Danger : LuneUiTheme.Bright);
|
LuneUi.SetColor(_cost, state == State.Unaffordable ? LuneUiTheme.Danger : LuneUiTheme.Bright);
|
||||||
_effect.text = item != null ? item.Text : "";
|
_effect.text = item != null ? item.Text : "";
|
||||||
Color badge = state == State.Equipped ? LuneUiTheme.Good : state == State.Available ? LuneUiTheme.BronzeLight : LuneUiTheme.Muted;
|
Color badge = state == State.Equipped ? LuneUiTheme.Good : state == State.Available ? LuneUiTheme.BronzeLight : LuneUiTheme.Muted;
|
||||||
if (state == State.Unaffordable)
|
if (state == State.Unaffordable)
|
||||||
badge = LuneUiTheme.Danger;
|
badge = LuneUiTheme.Danger;
|
||||||
string glyph = state == State.Equipped || state == State.Unique ? "check" : state == State.Incompatible || state == State.Locked ? "lock" : "";
|
string glyph = state == State.Equipped || state == State.Unique ? "check" : state == State.Incompatible || state == State.Locked || state == State.MissingParts ? "lock" : "";
|
||||||
_badge.SetState(StateText[state], badge, glyph, state == State.Equipped);
|
_badge.SetState(StateText[state], badge, glyph, state == State.Equipped);
|
||||||
Disabled = !usable;
|
Disabled = !usable;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -568,6 +568,9 @@ namespace Luneraria.UI
|
||||||
{
|
{
|
||||||
string itemId = pair.Key;
|
string itemId = pair.Key;
|
||||||
ItemSpec item = pair.Value;
|
ItemSpec item = pair.Value;
|
||||||
|
// S2 recipes exist only under the extended rules.
|
||||||
|
if (item.IsRecipe && !model.State.Extended)
|
||||||
|
continue;
|
||||||
if (column % 2 == 0)
|
if (column % 2 == 0)
|
||||||
line = LuneUi.Box(false, 8f, grid);
|
line = LuneUi.Box(false, 8f, grid);
|
||||||
column++;
|
column++;
|
||||||
|
|
@ -576,15 +579,19 @@ namespace Luneraria.UI
|
||||||
card.style.flexBasis = 0f;
|
card.style.flexBasis = 0f;
|
||||||
line.Add(card);
|
line.Add(card);
|
||||||
LuneItemSlot.State state = LuneItemSlot.State.Available;
|
LuneItemSlot.State state = LuneItemSlot.State.Available;
|
||||||
|
bool partsMissing = item.IsRecipe && !item.Parts.All(items.Contains);
|
||||||
|
int price = item.IsRecipe ? item.RecipeFee : item.Cost;
|
||||||
if (items.Contains(itemId))
|
if (items.Contains(itemId))
|
||||||
state = LuneItemSlot.State.Unique;
|
state = LuneItemSlot.State.Unique;
|
||||||
else if (!canAct)
|
else if (!canAct)
|
||||||
state = LuneItemSlot.State.Locked;
|
state = LuneItemSlot.State.Locked;
|
||||||
|
else if (partsMissing)
|
||||||
|
state = LuneItemSlot.State.MissingParts;
|
||||||
else if (level < item.MinLevel)
|
else if (level < item.MinLevel)
|
||||||
state = LuneItemSlot.State.Incompatible;
|
state = LuneItemSlot.State.Incompatible;
|
||||||
else if (items.Count >= LuneContent.ItemSlots)
|
else if (!item.IsRecipe && items.Count >= LuneContent.ItemSlots)
|
||||||
state = LuneItemSlot.State.Full;
|
state = LuneItemSlot.State.Full;
|
||||||
else if (gold < item.Cost)
|
else if (gold < price)
|
||||||
state = LuneItemSlot.State.Unaffordable;
|
state = LuneItemSlot.State.Unaffordable;
|
||||||
card.SetItem(itemId, state);
|
card.SetItem(itemId, state);
|
||||||
ItemCards[itemId] = card;
|
ItemCards[itemId] = card;
|
||||||
|
|
@ -596,11 +603,13 @@ namespace Luneraria.UI
|
||||||
};
|
};
|
||||||
string why = state == LuneItemSlot.State.Unique ? "Dein Held trägt es bereits; Items sind einzigartig."
|
string why = state == LuneItemSlot.State.Unique ? "Dein Held trägt es bereits; Items sind einzigartig."
|
||||||
: state == LuneItemSlot.State.Locked ? WhyNot(model)
|
: state == LuneItemSlot.State.Locked ? WhyNot(model)
|
||||||
|
: state == LuneItemSlot.State.MissingParts ? "Braucht " + string.Join(" und ", item.Parts.Select(id => LuneContent.Items[id].Name)) + " im Inventar."
|
||||||
: state == LuneItemSlot.State.Incompatible ? "Erfordert Heldenstufe " + item.MinLevel + "."
|
: state == LuneItemSlot.State.Incompatible ? "Erfordert Heldenstufe " + item.MinLevel + "."
|
||||||
: state == LuneItemSlot.State.Full ? "Alle " + LuneContent.ItemSlots + " Plätze belegt; verkaufe zuerst ein Item."
|
: state == LuneItemSlot.State.Full ? "Alle " + LuneContent.ItemSlots + " Plätze belegt; verkaufe zuerst ein Item."
|
||||||
: state == LuneItemSlot.State.Unaffordable ? "Es fehlen " + (item.Cost - gold) + " Gold."
|
: state == LuneItemSlot.State.Unaffordable ? "Es fehlen " + (price - gold) + " Gold."
|
||||||
: "Klick: kaufen und ausrüsten.";
|
: item.IsRecipe ? "Klick: schmieden (beide Teile werden eins, ein Platz wird frei)." : "Klick: kaufen und ausrüsten.";
|
||||||
LuneTooltip.Attach(card, () => new LuneTip { Title = item.Name + " · " + item.Cost + " Gold", Glyph = itemId, GlyphColor = LuneItemSlot.ColorOf(itemId), Text = item.Text, Note = why });
|
string title = item.IsRecipe ? item.Name + " · Rezept +" + item.RecipeFee + " Gold" : item.Name + " · " + item.Cost + " Gold";
|
||||||
|
LuneTooltip.Attach(card, () => new LuneTip { Title = title, Glyph = itemId, GlyphColor = LuneItemSlot.ColorOf(itemId), Text = item.Text, Note = why });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ namespace Luneraria.UI
|
||||||
VisualElement _system;
|
VisualElement _system;
|
||||||
VisualElement _left;
|
VisualElement _left;
|
||||||
LuneTeamPanel _team;
|
LuneTeamPanel _team;
|
||||||
|
/// <summary>The own team panel (S2 team actions; tests).</summary>
|
||||||
|
public LuneTeamPanel Team { get { return _team; } }
|
||||||
VisualElement _right;
|
VisualElement _right;
|
||||||
LunePanel _bottom;
|
LunePanel _bottom;
|
||||||
LuneResourceDisplay _gold;
|
LuneResourceDisplay _gold;
|
||||||
|
|
@ -438,6 +440,7 @@ namespace Luneraria.UI
|
||||||
_left.Add(teamPanel);
|
_left.Add(teamPanel);
|
||||||
_team = new LuneTeamPanel();
|
_team = new LuneTeamPanel();
|
||||||
teamPanel.Add(_team);
|
teamPanel.Add(_team);
|
||||||
|
_team.CommandRequested += command => CommandRequested?.Invoke(command);
|
||||||
Feed = new LuneEventFeed();
|
Feed = new LuneEventFeed();
|
||||||
_left.Add(Feed);
|
_left.Add(Feed);
|
||||||
Feed.FocusRequested += FocusTarget;
|
Feed.FocusRequested += FocusTarget;
|
||||||
|
|
@ -799,6 +802,7 @@ namespace Luneraria.UI
|
||||||
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.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);
|
||||||
|
_team.ApplyActions(Model);
|
||||||
SyncRight(planning, fighting);
|
SyncRight(planning, fighting);
|
||||||
SyncBottom(planning, fighting);
|
SyncBottom(planning, fighting);
|
||||||
SyncReady(planning, fighting);
|
SyncReady(planning, fighting);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ namespace Luneraria.UI
|
||||||
"ancient", "warning", "check", "close", "bot", "offline", "clock", "heart", "tempo", "range", "armor",
|
"ancient", "warning", "check", "close", "bot", "offline", "clock", "heart", "tempo", "range", "armor",
|
||||||
"move", "star", "lock", "up", "lane_top", "lane_mid", "lane_bot", "grotto", "pause", "play", "menu",
|
"move", "star", "lock", "up", "lane_top", "lane_mid", "lane_bot", "grotto", "pause", "play", "menu",
|
||||||
"gear", "human", "sword", "basaltschild", "glutklinge", "rankenmantel", "funkenfokus", "quellamulett",
|
"gear", "human", "sword", "basaltschild", "glutklinge", "rankenmantel", "funkenfokus", "quellamulett",
|
||||||
"kristallsplitter", "hero", "skull" };
|
"kristallsplitter", "hero", "skull", "bollwerk", "glutsturm", "quellherz", "splitterkrone" };
|
||||||
|
|
||||||
const float Tau = Mathf.PI * 2f;
|
const float Tau = Mathf.PI * 2f;
|
||||||
static readonly Dictionary<string, Vector2[]> Cache = new Dictionary<string, Vector2[]>();
|
static readonly Dictionary<string, Vector2[]> Cache = new Dictionary<string, Vector2[]>();
|
||||||
|
|
@ -28,6 +28,16 @@ namespace Luneraria.UI
|
||||||
return;
|
return;
|
||||||
var f = new Frame(rect);
|
var f = new Frame(rect);
|
||||||
Color dark = accent.a > 0f ? accent : new Color(color.r * .45f, color.g * .45f, color.b * .45f, color.a);
|
Color dark = accent.a > 0f ? accent : new Color(color.r * .45f, color.g * .45f, color.b * .45f, color.a);
|
||||||
|
// S2 recipes: the first part's symbol inside a forged ring with a small spark.
|
||||||
|
Luneraria.Core.ItemSpec recipe;
|
||||||
|
if (Luneraria.Core.ContentData.Items.TryGetValue(glyph, out recipe) && recipe.IsRecipe)
|
||||||
|
{
|
||||||
|
float inset = rect.width * .16f;
|
||||||
|
Draw(painter, recipe.Parts[0], new Rect(rect.x + inset, rect.y + inset, rect.width - 2f * inset, rect.height - 2f * inset), color, accent);
|
||||||
|
ArcLine(painter, f.P(.5f, .5f), f.S * .47f, 0f, Tau, color, Mathf.Max(1f, f.S * .06f));
|
||||||
|
Shard(painter, f, V(.82f, .18f), 0f, .14f, LuneUiTheme.Hex("fff2c0"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
switch (glyph)
|
switch (glyph)
|
||||||
{
|
{
|
||||||
case "coin":
|
case "coin":
|
||||||
|
|
|
||||||
|
|
@ -146,11 +146,43 @@ namespace Luneraria.View
|
||||||
view.SetHealth((float)hp, (float)definition.MaxHp, _synced);
|
view.SetHealth((float)hp, (float)definition.MaxHp, _synced);
|
||||||
if (view.Tier == "core")
|
if (view.Tier == "core")
|
||||||
SyncAncientBar(view, (float)hp, (float)definition.MaxHp);
|
SyncAncientBar(view, (float)hp, (float)definition.MaxHp);
|
||||||
if (unit != null && unit.Kind == "tower" && _combat != null)
|
// S2: a levelled Ancient shoots like a tower.
|
||||||
|
if (unit != null && (unit.Kind == "tower" || (unit.Kind == "core" && unit.Def.Damage > 0.0)) && _combat != null)
|
||||||
TowerAttack(view, unit);
|
TowerAttack(view, unit);
|
||||||
|
GlyphRing(definition, view, unit != null && fighting && _combat.Glyphed(unit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>S2 (Dota 2 glyph): a bright hexagon ring around an invulnerable tower.</summary>
|
||||||
|
readonly Dictionary<string, GameObject> _glyphRings = new Dictionary<string, GameObject>();
|
||||||
|
|
||||||
|
void GlyphRing(BuildingDef definition, LuneBuildingView view, bool on)
|
||||||
|
{
|
||||||
|
_glyphRings.TryGetValue(definition.Id, out GameObject ring);
|
||||||
|
if (!on)
|
||||||
|
{
|
||||||
|
if (ring != null)
|
||||||
|
ring.SetActive(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ring == null)
|
||||||
|
{
|
||||||
|
if (Library == null || Library.RingHexagon == null || Library.MoonBurst == null)
|
||||||
|
return;
|
||||||
|
ring = new GameObject("GlyphRing_" + definition.Id);
|
||||||
|
ring.transform.SetParent(view.transform, false);
|
||||||
|
ring.transform.localPosition = new Vector3(0f, .12f, 0f);
|
||||||
|
ring.transform.localScale = new Vector3(4.2f, 1f, 4.2f);
|
||||||
|
ring.AddComponent<MeshFilter>().sharedMesh = Library.RingHexagon;
|
||||||
|
var renderer = ring.AddComponent<MeshRenderer>();
|
||||||
|
renderer.sharedMaterial = Library.MoonBurst;
|
||||||
|
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
||||||
|
_glyphRings[definition.Id] = ring;
|
||||||
|
}
|
||||||
|
ring.SetActive(true);
|
||||||
|
ring.transform.Rotate(0f, 40f * Time.deltaTime, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
void TowerAttack(LuneBuildingView view, LuneUnit tower)
|
void TowerAttack(LuneBuildingView view, LuneUnit tower)
|
||||||
{
|
{
|
||||||
_towerSerials.TryGetValue(tower.BuildingId, out int last);
|
_towerSerials.TryGetValue(tower.BuildingId, out int last);
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,10 @@ Legende:
|
||||||
| Respawn und Rückkauf | umgesetzt in S1, abgeschwächt (Erschöpfung statt Aussetzen) |
|
| Respawn und Rückkauf | umgesetzt in S1, abgeschwächt (Erschöpfung statt Aussetzen) |
|
||||||
| Drachen-Analogon | umgesetzt in S1 als Mondsegen über die Grotte |
|
| Drachen-Analogon | umgesetzt in S1 als Mondsegen über die Grotte |
|
||||||
| Schadens- und Rüstungstypen | bestand bereits (3 × 3); Farben im HUD seit G3 |
|
| Schadens- und Rüstungstypen | bestand bereits (3 × 3); Farben im HUD seit G3 |
|
||||||
|
| Glyphe | umgesetzt in S2 |
|
||||||
|
| Item-Rezepte | umgesetzt in S2 |
|
||||||
|
| Ancient stärken (Legion TD 2) | umgesetzt in S2 (neu, nicht in der ursprünglichen Liste) |
|
||||||
|
| Fog of War, Neutrallager, Schreine | offen (S3) |
|
||||||
|
|
||||||
## Empfohlene Reihenfolge
|
## Empfohlene Reihenfolge
|
||||||
|
|
||||||
|
|
|
||||||
87
docs/luneraria/S2-STRATEGIE.md
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
# S2 · Glyphe, Item-Rezepte und Ancient-Stufen
|
||||||
|
|
||||||
|
Stand: 11. Oktober 2026 · Branch `luneraria/s2-strategie` (von `luneraria/s1-strategie` = `8e4b52f`).
|
||||||
|
Grundlage: [IDEEN.md](IDEEN.md) und [S1-STRATEGIE.md](S1-STRATEGIE.md). Auftrag: „mach weiter mit S2“.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Kurzfassung
|
||||||
|
|
||||||
|
| Vorbild | Mechanik | Strategische Frage |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Dota 2 (Glyphe der Befestigung) | **Glyphe:** Das Team hält sie in der Planung für eine Lane bereit. Fällt dort im Kampf ein eigener Turm unter 50 %, sind die Türme dieser Lane 5 s unverwundbar. Danach folgen 4 Runden Abklingzeit. Löst sie nicht aus, bleibt sie bereit | Wo greift der Gegner an? Die Glyphe ist eine Wette auf seinen Plan |
|
||||||
|
| Dota 2 / League of Legends (Rezepte) | **Item-Rezepte:** Zwei Items plus ein Rezeptpreis ergeben ein stärkeres Item, und ein Platz wird frei. Es gibt Bollwerk, Glutsturm, Quellherz und Splitterkrone | Breit kaufen oder auf ein Rezept hinarbeiten? |
|
||||||
|
| Legion TD 2 (König-Upgrades) | **Ancient-Stufen:** Jeder Spieler eines Teams kann Stufen des eigenen Ancients kaufen (höchstens 3, Preis 150 × nächste Stufe). Jede Stufe repariert 1.000 und lässt den Ancient auf Angreifer in 7,5 m schießen (45 / 75 / 110 Schaden pro Sekunde) | Gold in Angriff, Armee oder in die letzte Verteidigung? |
|
||||||
|
| S1-Befund | **Bots und Grotte:** Mit erweiterten Regeln jagen auch die Mid-Bots ab Runde 8 den Boss, aber nur, wenn ihre Lane sicher ist (beide eigenen Türme über 70 %) | Mondsegen kommt in Bot-Partien jetzt wirklich vor |
|
||||||
|
|
||||||
|
Wie in S1 wirkt alles nur mit `MatchOptions.Extended`. Ohne den Schalter bleiben die Referenzregeln unverändert. Das betrifft Golden-Replays, Fingerabdrücke und die HUD-Prüfung; die Suite `s2` prüft das ausdrücklich.
|
||||||
|
|
||||||
|
## Rezepte
|
||||||
|
|
||||||
|
| Rezept | Teile | Preis | Wirkung |
|
||||||
|
| --- | --- | ---: | --- |
|
||||||
|
| Bollwerk | Basaltschild + Rankenmantel | 120 | +600 Leben, +15 % Schadensminderung |
|
||||||
|
| Glutsturm | Glutklinge + Funkenfokus | 140 | +25 % Schaden, +22 % Angriffstempo |
|
||||||
|
| Quellherz | Quellamulett + Basaltschild | 120 | +450 Leben, 2,2 % Regeneration pro Sekunde |
|
||||||
|
| Splitterkrone | Kristallsplitter + Glutklinge | 160 | +20 % Schaden; Treffer 35 % an 2 Gegner in 1,5 m; ab Stufe 5 |
|
||||||
|
|
||||||
|
Das fertige Item nimmt den Platz des ersten Teils ein, damit wird ein Platz frei. Verkauft bringt es die Hälfte des Gesamtpreises (Teile plus Rezept). Rückgängig machen geht wie bei jedem Planungsschritt.
|
||||||
|
|
||||||
|
## Regeldetails
|
||||||
|
|
||||||
|
- **Glyphe (Teamentscheidung):** Es gilt die Wahl des ersten Spielers in Platzreihenfolge, der eine Lane hält. Mitspieler sehen ihre Pläne gegenseitig nicht, das ist bestehende Privatsphäre. Die Unverwundbarkeit greift für jeden Schaden, auch Fläche und Schaden über Zeit. Nur eine ausgelöste Glyphe startet die Abklingzeit.
|
||||||
|
- **Ancient-Stufen:** Der Preis richtet sich nach der festen Teamstufe plus den eigenen Käufen dieser Planung. Kaufen zwei Spieler gleichzeitig über Stufe 3 hinaus, wird der Überschuss erstattet. Der Ancient zielt wie ein Turm (nur in Reichweite, Einheiten zuerst) und nutzt den Turmtakt.
|
||||||
|
- **Netz:** `Draft.GlyphLane`, `Draft.CoreInvest`, `BossState.GlyphReady` und `BossState.CoreLevel` werden übertragen. Den Glyphen-Ring sehen Host und Offline-Spieler; Gäste sehen den Kampfzustand der Gebäude nur über die Lebenspunkte (bekannte Einschränkung, wie bei den Turmschüssen).
|
||||||
|
|
||||||
|
## Oberfläche
|
||||||
|
|
||||||
|
| Stelle | Anzeige |
|
||||||
|
| --- | --- |
|
||||||
|
| Teampanel | Zeile **GLYPHE** mit TOP, MID, BOT und Status („bereit“ / „ab Runde N“); Zeile **ANCIENT** mit „STUFE 1/3 (+1)“ und Kaufknopf „+300 G“; Tooltips nennen die Vorbilder |
|
||||||
|
| Heldenfenster, Items | Rezepte mit Rahmen-Symbol, Preis „+120“, Zustand KAUFBAR, TEILE FEHLEN oder STUFE FEHLT; Klick schmiedet |
|
||||||
|
| Feld | Bei ausgelöster Glyphe ein drehender heller Sechseckring um die geschützten Türme; ein Ancient mit Stufe schießt sichtbar wie ein Turm |
|
||||||
|
|
||||||
|
## Balance (Bot-Partien)
|
||||||
|
|
||||||
|
Werkzeug: `dotnet run -c Release --project tools/dotnet/Luneraria.Balance s1 60`. Ausgewertet sind 60 Seeds mit der normalen Profil-Rotation, Rohdaten in `docs/port/logs/s2/balance-s2.txt`.
|
||||||
|
|
||||||
|
| | Referenz | S1 | S2 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| Siege Blau / Rot | 30 / 30 | 29 / 31 | 30 / 30 |
|
||||||
|
| Ø Runden | 10,7 | 12,8 | 12,1 |
|
||||||
|
| Wer den ersten Turm nimmt, gewinnt | 40/60 | 43/60 | 43/60 |
|
||||||
|
| Mondsegen-Stapel | – | 8 | 25 |
|
||||||
|
| Ausgelöste Glyphen | – | – | 128 |
|
||||||
|
| Gekaufte Ancient-Stufen | – | – | 50 |
|
||||||
|
| Rezepte bei Bots | – | – | 0 |
|
||||||
|
|
||||||
|
So ist die Balance entstanden:
|
||||||
|
- **Erste Fassung:** Mid-Bots zogen ohne Lane-Prüfung in die Grotte. Dadurch stieg „erster Turm gewinnt“ auf 52 von 60, die Grottengänge rissen Lanes auf.
|
||||||
|
- **Lane-Prüfung (über 70 %):** Damit liegt der Wert wieder bei 43 von 60.
|
||||||
|
- **Ancient-Stufen:** Die Bots kauften sie erst, nachdem ihr Teamplan vor den Heerkauf gezogen wurde.
|
||||||
|
- **Rezepte:** Bots werben ihre Helden erst um Runde 10 bis 12 an (bestehendes Referenzverhalten) und kommen deshalb selten zu zwei passenden Items. Rezepte sind daher vor allem ein Werkzeug für Menschen. Dass ein Bot mit beiden Teilen schmiedet, prüft `s2` gezielt.
|
||||||
|
|
||||||
|
## Tests und Nachweise
|
||||||
|
|
||||||
|
| Prüfung | Ergebnis |
|
||||||
|
| --- | --- |
|
||||||
|
| Kern (.NET 8) | 566/566, darunter **s2 35** (Referenz unberührt, Glyphe: Teamwahl, Auslösen, Schutz, Ende, Abklingzeit nur nach Auslösen; Rezepte: Teile, Preis, Platz frei, Rückgängig, Verkauf, Stufe; Ancient: Preise, Teamdeckel mit Erstattung, Reparatur, Schuss; Bots: Grotte, Mondsegen, Glyphen, Schmieden) |
|
||||||
|
| Golden und Fingerabdrücke | 0 Abweichungen; `DCE95C96556F3A1A` bei 30/60/120 fps und unbegrenzt unverändert |
|
||||||
|
| Unity EditMode | 32/32 |
|
||||||
|
| Unity PlayMode | 17/17 (neu `S2RulesViewsTests.S2Hud`); Verify A–I 718/0, eine Prüfung angepasst: „item shop lists every item of the rules in play“ (unter Referenzregeln ohne Rezepte) |
|
||||||
|
| Player-Selbsttest | macOS 30/30 |
|
||||||
|
| Leistung | sechs Sylvaras, Seed 9 ab Welle 2: Karte 148,4–150,1 fps, Mid nah 169,5–172,8 fps, HUD 0,04–0,05 ms. Gleich S1 (148,5–150,3 / 170,1–171,7) |
|
||||||
|
|
||||||
|
Die Bilder liegen in `docs/port/screenshots/s2/spiel/`:
|
||||||
|
1. Team-Aktionen
|
||||||
|
2. Ancient-Tooltip
|
||||||
|
3. Rezept im Shop
|
||||||
|
4. Bollwerk geschmiedet
|
||||||
|
5. Glyphe am Turm
|
||||||
|
|
||||||
|
## Offene Punkte
|
||||||
|
|
||||||
|
- **Fog of War mit Spähern (S3):** braucht einen Umbau von Sichtmodell, Planungsvorschau und Netz-Privatsphäre.
|
||||||
|
- **Neutrallager und Schreine (LoL-Buffs):** brauchen neue Kartenorte samt Ansichten.
|
||||||
|
- **Glyphen-Ring auf Gästen:** Dafür muss der Glyphen-Zustand in den Kampfrahmen.
|
||||||
|
- **Bots werben Helden spät:** Ein früherer Heldenkauf würde Items und Rezepte in Bot-Partien beleben. Das ist eine eigene Bot-Phase, im erweiterten Modus aber ohne Paritätsfolgen machbar.
|
||||||
|
|
@ -288,6 +288,18 @@ Die Glutpakt-Arbeit entstand als Kette aufeinander aufbauender Zweige (`lunerari
|
||||||
| Bild | `screenshots/s1/spiel/` |
|
| Bild | `screenshots/s1/spiel/` |
|
||||||
| Commits | `luneraria/s1-strategie` (Basis: Merge H1 + U5.5 `2e05cbe`) |
|
| Commits | `luneraria/s1-strategie` (Basis: Merge H1 + U5.5 `2e05cbe`) |
|
||||||
|
|
||||||
|
## S2 · Glyphe, Rezepte, Ancient-Stufen (Dota 2, League of Legends, Legion TD 2)
|
||||||
|
|
||||||
|
| Punkt | Ergebnis |
|
||||||
|
| --- | --- |
|
||||||
|
| Tests | Kern .NET 566/566 (neu `s2` 35), EditMode 32/32, PlayMode 17/17 (neu `S2Hud`), Verify A–I 718/0 (Shop-Prüfung auf die Regeln im Spiel angepasst), Player-Selbsttest 30/30 |
|
||||||
|
| Parität | Referenzregeln unverändert: Golden 0 Abweichungen, Fingerabdruck `DCE95C96556F3A1A` gleich |
|
||||||
|
| Inhalt | Glyphe (Teamwahl einer Lane, 5 s unverwundbar unter 50 %, 4 Runden Abklingzeit nach Auslösen), vier Item-Rezepte, Ancient-Stufen 1–3 mit Reparatur und Schuss, Grottenjagd der Mid-Bots bei sicherer Lane |
|
||||||
|
| Balance | 60 Bot-Partien: Blau/Rot 30/30, Ø 12,1 Runden, erster Turm gewinnt 43/60, Mondsegen 25, Glyphen 128, Ancient-Stufen 50 (`logs/s2/balance-s2.txt`) |
|
||||||
|
| Leistung | Karte 148,4–150,1 fps, Mid nah 169,5–172,8 fps, HUD 0,04–0,05 ms (`logs/s2/`) |
|
||||||
|
| Bild | `screenshots/s2/spiel/` |
|
||||||
|
| Commits | `luneraria/s2-strategie` |
|
||||||
|
|
||||||
## 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 (S1, Strategie-Regeln).
|
Stand: 11. Oktober 2026 (S2, Glyphe, Rezepte, Ancient).
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
@ -16,6 +16,7 @@ Zielplattformen sind Fedora 44 und Windows 11; macOS ist die Entwicklungsplattfo
|
||||||
| 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) |
|
| 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) |
|
||||||
|
| S2 · Glyphe, Rezepte, Ancient | **fertig** (`luneraria/s2-strategie`): Glyphe (Dota 2), Item-Rezepte (Dota 2/LoL), Ancient-Stufen (Legion TD 2), Grottenjagd der Bots; hinter dem Regelschalter „Erweitert“ | [../luneraria/S2-STRATEGIE.md](../luneraria/S2-STRATEGIE.md) |
|
||||||
|
|
||||||
QA je Meilenstein: [PORT-QA.md](PORT-QA.md).
|
QA je Meilenstein: [PORT-QA.md](PORT-QA.md).
|
||||||
|
|
||||||
|
|
|
||||||
3
docs/port/logs/s2/balance-s2.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; glyphs fired; Ancient levels; recipe items
|
||||||
|
referenz; 60; 30; 30; 0; 0; 10.7; 9; 12; 40/60; 0.0; 0; 0; 0; 0; 0; 0; 0; 0; 0
|
||||||
|
erweitert; 60; 30; 30; 0; 0; 12.1; 11; 15; 43/60; 12.2; 52; 588; 250; 25; 48; 24; 128; 50; 0
|
||||||
17
docs/port/logs/s2/core-tests-dotnet.json
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"suites": [
|
||||||
|
{"suite": "content", "checks": 77, "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.70, "failed": [], "notes": ["full match seed 2 -> Rot after 9 rounds"]},
|
||||||
|
{"suite": "m2", "checks": 73, "failures": 0, "seconds": 0.39, "failed": [], "notes": []},
|
||||||
|
{"suite": "golden", "checks": 157, "failures": 0, "seconds": 1.53, "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.49, "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.02, "failed": [], "notes": ["sylvara-bot match seed 12: 13/1"]},
|
||||||
|
{"suite": "s1", "checks": 31, "failures": 0, "seconds": 0.43, "failed": [], "notes": ["extended seed 17: 12/1, income bonus 12,12,12,24,24,24, blessing 0/1"]},
|
||||||
|
{"suite": "s2", "checks": 34, "failures": 0, "seconds": 2.82, "failed": [], "notes": ["12 extended matches: expeditions 5, Mondsegen 5, recipe heroes 0, glyphs 25, Ancient levels 8; seed 1 13/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"
|
||||||
|
}
|
||||||
355
docs/port/logs/s2/editmode-results.xml
Normal file
|
|
@ -0,0 +1,355 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="32" result="Passed" total="32" passed="32" 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:44:06Z" end-time="2026-10-11 10:45:45Z" duration="98.5029123">
|
||||||
|
<test-suite type="TestSuite" id="1000" name="luneraria-unity" fullname="luneraria-unity" runstate="Runnable" testcasecount="32" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:45Z" duration="98.502912" total="32" passed="32" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="Assembly" id="1015" name="Luneraria.Core.Tests.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.Core.Tests.dll" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:21Z" duration="75.271165" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="4902" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1016" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:21Z" duration="75.270960" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1017" name="Core" fullname="Luneraria.Core" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:21Z" duration="75.270840" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1018" name="Tests" fullname="Luneraria.Core.Tests" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:21Z" duration="75.270698" total="12" passed="12" 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:44:06Z" end-time="2026-10-11 10:44:06Z" duration="0.049031" 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="1999084509" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:44:06Z" duration="0.044617" 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="143255428" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:44:06Z" duration="0.000322" 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="10" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:45:21Z" duration="75.221210" total="10" passed="10" 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="750756088" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:44:06Z" duration="0.011051" 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: 77 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="300220524" result="Passed" start-time="2026-10-11 10:44:06Z" end-time="2026-10-11 10:44:11Z" duration="5.283677" 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="1910790071" result="Passed" start-time="2026-10-11 10:44:11Z" end-time="2026-10-11 10:44:25Z" duration="13.657077" 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="2027152764" result="Passed" start-time="2026-10-11 10:44:25Z" end-time="2026-10-11 10:44:39Z" duration="13.615551" 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="470899894" result="Passed" start-time="2026-10-11 10:44:39Z" end-time="2026-10-11 10:44:48Z" duration="9.095139" 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="1030252584" result="Passed" start-time="2026-10-11 10:44:48Z" end-time="2026-10-11 10:44:51Z" duration="3.503753" 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="1100722831" result="Passed" start-time="2026-10-11 10:44:51Z" end-time="2026-10-11 10:44:51Z" duration="0.066036" 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="1054629901" result="Passed" start-time="2026-10-11 10:44:51Z" end-time="2026-10-11 10:44:52Z" duration="0.565068" 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="749897290" result="Passed" start-time="2026-10-11 10:44:52Z" end-time="2026-10-11 10:44:56Z" duration="3.758777" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[s1: extended seed 17: 12/1, income bonus 12,12,12,24,24,24, blessing 0/1
|
||||||
|
LUNE_U1_PASS s1: 31 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1014" name="S2Rules" fullname="Luneraria.Core.Tests.CoreSuiteTests.S2Rules" methodname="S2Rules" classname="Luneraria.Core.Tests.CoreSuiteTests" runstate="Runnable" seed="2124808725" result="Passed" start-time="2026-10-11 10:44:56Z" end-time="2026-10-11 10:45:21Z" duration="25.662332" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
<output><![CDATA[s2: 12 extended matches: expeditions 5, Mondsegen 5, recipe heroes 0, glyphs 25, Ancient levels 8; seed 1 13/0
|
||||||
|
LUNE_U1_PASS s2: 34 checks
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="Assembly" id="1024" 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:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.071465" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="4902" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1025" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.008617" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1026" name="Net" fullname="Luneraria.Net" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.008288" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1027" name="Tests" fullname="Luneraria.Net.Tests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.008182" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1019" name="LuneWireTests" fullname="Luneraria.Net.Tests.LuneWireTests" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.007981" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1020" name="CommandRoundTrips" fullname="Luneraria.Net.Tests.LuneWireTests.CommandRoundTrips" methodname="CommandRoundTrips" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="883282839" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.001769" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1023" name="LobbyAndOptionsRoundTrip" fullname="Luneraria.Net.Tests.LuneWireTests.LobbyAndOptionsRoundTrip" methodname="LobbyAndOptionsRoundTrip" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="72664558" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.000792" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1021" name="StateRoundTrips" fullname="Luneraria.Net.Tests.LuneWireTests.StateRoundTrips" methodname="StateRoundTrips" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="1840394991" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.003047" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1022" name="UnitsAndFrameRoundTrip" fullname="Luneraria.Net.Tests.LuneWireTests.UnitsAndFrameRoundTrip" methodname="UnitsAndFrameRoundTrip" classname="Luneraria.Net.Tests.LuneWireTests" runstate="Runnable" seed="1934664744" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.001647" 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="1041" 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:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.198386" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="4902" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1042" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.198116" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1043" name="UI" fullname="Luneraria.UI" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.197980" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1044" name="Tests" fullname="Luneraria.UI.Tests" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.197832" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1028" name="HudModelTests" fullname="Luneraria.UI.Tests.HudModelTests" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" testcasecount="12" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.197573" total="12" passed="12" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1033" name="AnnouncedDangersRaiseTheThreat" fullname="Luneraria.UI.Tests.HudModelTests.AnnouncedDangersRaiseTheThreat" methodname="AnnouncedDangersRaiseTheThreat" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="444152263" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.003811" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1034" name="BattlefieldCountsTheLaneUnits" fullname="Luneraria.UI.Tests.HudModelTests.BattlefieldCountsTheLaneUnits" methodname="BattlefieldCountsTheLaneUnits" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1873361056" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.030734" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1031" name="BossLaneChoiceIsPrivateToTheChooser" fullname="Luneraria.UI.Tests.HudModelTests.BossLaneChoiceIsPrivateToTheChooser" methodname="BossLaneChoiceIsPrivateToTheChooser" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="71744586" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.002870" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1035" name="BossStatesFromPublicState" fullname="Luneraria.UI.Tests.HudModelTests.BossStatesFromPublicState" methodname="BossStatesFromPublicState" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="341904562" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.000660" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1040" name="BotMatchStillStartsByItself" fullname="Luneraria.UI.Tests.HudModelTests.BotMatchStillStartsByItself" methodname="BotMatchStillStartsByItself" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="768180575" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.001023" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1029" name="EverySeatSeesTheSamePublicPicture" fullname="Luneraria.UI.Tests.HudModelTests.EverySeatSeesTheSamePublicPicture" methodname="EverySeatSeesTheSamePublicPicture" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1435246166" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.041602" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1039" name="HumanSeatPlanningWaitsForReady" fullname="Luneraria.UI.Tests.HudModelTests.HumanSeatPlanningWaitsForReady" methodname="HumanSeatPlanningWaitsForReady" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="284025382" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.055998" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1032" name="LaneThreatFollowsTowerHealth" fullname="Luneraria.UI.Tests.HudModelTests.LaneThreatFollowsTowerHealth" methodname="LaneThreatFollowsTowerHealth" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1154303664" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.000894" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1036" name="NextWaveIsTheRulesWave" fullname="Luneraria.UI.Tests.HudModelTests.NextWaveIsTheRulesWave" methodname="NextWaveIsTheRulesWave" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1370177385" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.001369" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1038" name="NumbersAreFormattedAsInGodot" fullname="Luneraria.UI.Tests.HudModelTests.NumbersAreFormattedAsInGodot" methodname="NumbersAreFormattedAsInGodot" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="2010371225" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:21Z" duration="0.000476" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1030" name="OtherSeatsPlansStayPrivateUntilCombat" fullname="Luneraria.UI.Tests.HudModelTests.OtherSeatsPlansStayPrivateUntilCombat" methodname="OtherSeatsPlansStayPrivateUntilCombat" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="1754542740" result="Passed" start-time="2026-10-11 10:45:21Z" end-time="2026-10-11 10:45:22Z" duration="0.056183" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1037" name="ReadyLocksThePlan" fullname="Luneraria.UI.Tests.HudModelTests.ReadyLocksThePlan" methodname="ReadyLocksThePlan" classname="Luneraria.UI.Tests.HudModelTests" runstate="Runnable" seed="2000998423" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:22Z" duration="0.000784" 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="1050" 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:45:22Z" end-time="2026-10-11 10:45:45Z" duration="22.959305" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="4902" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="EditMode" />
|
||||||
|
<property name="EditorOnly" value="True" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1051" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:45Z" duration="22.958939" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1052" name="View" fullname="Luneraria.View" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:45Z" duration="22.958744" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1053" name="Tests" fullname="Luneraria.View.Tests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:45Z" duration="22.958623" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1045" name="FrameRateIndependenceTests" fullname="Luneraria.View.Tests.FrameRateIndependenceTests" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" testcasecount="4" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:45Z" duration="22.958471" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1049" name="ClockRunsWholeStepsOnly" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.ClockRunsWholeStepsOnly" methodname="ClockRunsWholeStepsOnly" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="1685881264" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:22Z" duration="0.000599" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="retryIteration" value="0" />
|
||||||
|
<property name="repeatIteration" value="0" />
|
||||||
|
</properties>
|
||||||
|
</test-case>
|
||||||
|
<test-case id="1046" name="FullBotMatchSeed2IsTheSameAtEveryFrameRate" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.FullBotMatchSeed2IsTheSameAtEveryFrameRate" methodname="FullBotMatchSeed2IsTheSameAtEveryFrameRate" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="837544746" result="Passed" start-time="2026-10-11 10:45:22Z" end-time="2026-10-11 10:45:30Z" duration="8.914069" 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="1047" name="HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" methodname="HighWaveMatchFromRound21IsTheSameAtEveryFrameRate" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="946887950" result="Passed" start-time="2026-10-11 10:45:30Z" end-time="2026-10-11 10:45:36Z" duration="5.126684" 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="1048" name="MatchSpeedDoesNotChangeTheMatch" fullname="Luneraria.View.Tests.FrameRateIndependenceTests.MatchSpeedDoesNotChangeTheMatch" methodname="MatchSpeedDoesNotChangeTheMatch" classname="Luneraria.View.Tests.FrameRateIndependenceTests" runstate="Runnable" seed="1517085260" result="Passed" start-time="2026-10-11 10:45:36Z" end-time="2026-10-11 10:45:45Z" duration="8.915992" 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/s2/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": 2994, "fps": 149.7, "frame_ms_avg": 6.68, "frame_ms_median": 3.00, "frame_ms_p95": 21.50, "frame_ms_p99": 28.41, "frame_ms_max": 40.40, "frames_over_16_7_ms": 311, "of_them_with_simulation_step": 32, "gc_collections": 21, "simulation_ms_per_frame": 0.061, "views_ms_per_frame": 0.193, "ui_ms_per_frame": 0.053, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.30 / median 2.19 / p95 3.18 ms, render thread avg 1.69 / median 1.68 / p95 1.99 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.37 / median 0.28 / p95 19.12 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1702, "fps": 170.2, "frame_ms_avg": 5.88, "frame_ms_median": 2.56, "frame_ms_p95": 20.43, "frame_ms_p99": 27.60, "frame_ms_max": 33.30, "frames_over_16_7_ms": 165, "of_them_with_simulation_step": 7, "gc_collections": 7, "simulation_ms_per_frame": 0.034, "views_ms_per_frame": 0.162, "ui_ms_per_frame": 0.038, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.74 / median 1.68 / p95 2.40 ms, render thread avg 1.31 / median 1.29 / p95 1.52 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.15 / median 0.28 / p95 18.57 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
15
docs/port/logs/s2/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": 2968, "fps": 148.4, "frame_ms_avg": 6.74, "frame_ms_median": 2.91, "frame_ms_p95": 21.51, "frame_ms_p99": 27.40, "frame_ms_max": 35.38, "frames_over_16_7_ms": 330, "of_them_with_simulation_step": 33, "gc_collections": 21, "simulation_ms_per_frame": 0.063, "views_ms_per_frame": 0.197, "ui_ms_per_frame": 0.054, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.33 / median 2.23 / p95 3.20 ms, render thread avg 1.72 / median 1.71 / p95 2.03 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.40 / median 0.25 / p95 19.07 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1695, "fps": 169.5, "frame_ms_avg": 5.90, "frame_ms_median": 2.70, "frame_ms_p95": 19.91, "frame_ms_p99": 28.40, "frame_ms_max": 34.41, "frames_over_16_7_ms": 139, "of_them_with_simulation_step": 11, "gc_collections": 7, "simulation_ms_per_frame": 0.032, "views_ms_per_frame": 0.164, "ui_ms_per_frame": 0.039, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.74 / median 1.69 / p95 2.39 ms, render thread avg 1.31 / median 1.29 / p95 1.53 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.15 / median 0.79 / p95 18.04 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
15
docs/port/logs/s2/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": 3002, "fps": 150.1, "frame_ms_avg": 6.66, "frame_ms_median": 2.82, "frame_ms_p95": 21.89, "frame_ms_p99": 28.70, "frame_ms_max": 40.11, "frames_over_16_7_ms": 317, "of_them_with_simulation_step": 29, "gc_collections": 21, "simulation_ms_per_frame": 0.061, "views_ms_per_frame": 0.188, "ui_ms_per_frame": 0.053, "units_alive_max": 77},
|
||||||
|
// timings overview (zoom 64): main thread avg 2.26 / median 2.18 / p95 3.01 ms, render thread avg 1.67 / median 1.67 / p95 1.95 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.39 / median 0.24 / p95 19.50 ms
|
||||||
|
{"view": "mid lane close (zoom 22)", "frames": 1731, "fps": 172.8, "frame_ms_avg": 5.79, "frame_ms_median": 2.73, "frame_ms_p95": 19.11, "frame_ms_p99": 27.71, "frame_ms_max": 34.30, "frames_over_16_7_ms": 136, "of_them_with_simulation_step": 20, "gc_collections": 7, "simulation_ms_per_frame": 0.036, "views_ms_per_frame": 0.159, "ui_ms_per_frame": 0.036, "units_alive_max": 37}
|
||||||
|
// timings mid lane close (zoom 22): main thread avg 1.71 / median 1.63 / p95 2.40 ms, render thread avg 1.29 / median 1.28 / p95 1.51 ms, gpu avg 0.00 / median 0.00 / p95 0.00 ms, present wait avg 4.07 / median 0.95 / p95 17.19 ms
|
||||||
|
]
|
||||||
|
}
|
||||||
11
docs/port/logs/s2/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"
|
||||||
|
}
|
||||||
408
docs/port/logs/s2/playmode-results.xml
Normal file
|
|
@ -0,0 +1,408 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<test-run id="2" testcasecount="27" result="Passed" total="27" passed="17" failed="0" inconclusive="0" skipped="10" asserts="0" engine-version="3.5.0.0" clr-version="4.0.30319.42000" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:51:09Z" duration="309.2797882">
|
||||||
|
<test-suite type="TestSuite" id="1052" name="luneraria-unity" fullname="luneraria-unity" runstate="Runnable" testcasecount="27" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:51:09Z" duration="309.279788" total="27" passed="17" failed="0" inconclusive="0" skipped="10" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="Assembly" id="1037" name="Luneraria.UI.Tests.PlayMode.dll" fullname="/private/tmp/lune-konzept/Library/ScriptAssemblies/Luneraria.UI.Tests.PlayMode.dll" runstate="Runnable" testcasecount="25" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:26Z" duration="26.479638" total="25" passed="15" failed="0" inconclusive="0" skipped="10" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="5112" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
<property name="EditorOnly" value="False" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1038" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="25" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:26Z" duration="26.478818" total="25" passed="15" failed="0" inconclusive="0" skipped="10" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1039" name="UI" fullname="Luneraria.UI" runstate="Runnable" testcasecount="25" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:26Z" duration="26.478486" total="25" passed="15" failed="0" inconclusive="0" skipped="10" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1040" name="Tests" fullname="Luneraria.UI.Tests" runstate="Runnable" testcasecount="25" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:26Z" duration="26.477807" total="25" passed="15" failed="0" inconclusive="0" skipped="10" 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:46:00Z" end-time="2026-10-11 10:46:00Z" duration="0.014808" 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="1738638995" 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:46:00Z" end-time="2026-10-11 10:46:00Z" duration="0.000100" 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="280214387" 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:46:00Z" end-time="2026-10-11 10:46:02Z" duration="2.095889" 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="1876596647" result="Skipped" label="Explicit" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:00Z" duration="0.000199" 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="1476079400" result="Passed" start-time="2026-10-11 10:46:00Z" end-time="2026-10-11 10:46:02Z" duration="2.091187" 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:46:02Z" end-time="2026-10-11 10:46:02Z" duration="0.401615" 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="1782765236" result="Passed" start-time="2026-10-11 10:46:02Z" end-time="2026-10-11 10:46:02Z" duration="0.398557" 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.145 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:46:02Z" end-time="2026-10-11 10:46:02Z" duration="0.000086" 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="1811322826" 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:46:02Z" end-time="2026-10-11 10:46:13Z" duration="11.269174" 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="148923631" result="Passed" start-time="2026-10-11 10:46:02Z" end-time="2026-10-11 10:46:06Z" duration="3.977893" 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="2085720754" result="Passed" start-time="2026-10-11 10:46:06Z" end-time="2026-10-11 10:46:06Z" duration="0.355632" 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="2117339205" result="Passed" start-time="2026-10-11 10:46:06Z" end-time="2026-10-11 10:46:07Z" duration="0.719279" 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="2132733628" result="Passed" start-time="2026-10-11 10:46:07Z" end-time="2026-10-11 10:46:08Z" duration="0.516842" 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="267025641" result="Passed" start-time="2026-10-11 10:46:08Z" end-time="2026-10-11 10:46:09Z" duration="1.381829" 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="929951852" result="Passed" start-time="2026-10-11 10:46:09Z" end-time="2026-10-11 10:46:12Z" duration="3.403122" 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="1139381222" result="Passed" start-time="2026-10-11 10:46:12Z" end-time="2026-10-11 10:46:13Z" duration="0.437254" 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="1312088518" result="Passed" start-time="2026-10-11 10:46:13Z" end-time="2026-10-11 10:46:13Z" duration="0.193679" 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.214 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="1095923293" result="Passed" start-time="2026-10-11 10:46:13Z" end-time="2026-10-11 10:46:13Z" duration="0.280726" 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:46:13Z" end-time="2026-10-11 10:46:13Z" duration="0.000209" 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="199659959" 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="89262612" 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="1147416487" 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="155294634" 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:46:13Z" end-time="2026-10-11 10:46:15Z" duration="1.632490" 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="1036015720" result="Skipped" label="Explicit" start-time="2026-10-11 10:46:13Z" end-time="2026-10-11 10:46:13Z" 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="506546446" result="Passed" start-time="2026-10-11 10:46:13Z" end-time="2026-10-11 10:46:15Z" duration="1.630418" 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="S2RulesViewsTests" fullname="Luneraria.UI.Tests.S2RulesViewsTests" classname="Luneraria.UI.Tests.S2RulesViewsTests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:46:15Z" end-time="2026-10-11 10:46:15Z" duration="0.289652" total="2" passed="1" failed="0" inconclusive="0" skipped="1" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1032" name="S2Bilder" fullname="Luneraria.UI.Tests.S2RulesViewsTests.S2Bilder" methodname="S2Bilder" classname="Luneraria.UI.Tests.S2RulesViewsTests" runstate="Explicit" seed="1305928999" result="Skipped" label="Explicit" start-time="2026-10-11 10:46:15Z" end-time="2026-10-11 10:46:15Z" 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="1031" name="S2Hud" fullname="Luneraria.UI.Tests.S2RulesViewsTests.S2Hud" methodname="S2Hud" classname="Luneraria.UI.Tests.S2RulesViewsTests" runstate="Runnable" seed="1988166345" result="Passed" start-time="2026-10-11 10:46:15Z" end-time="2026-10-11 10:46:15Z" duration="0.286115" 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="1033" name="StartScreenTests" fullname="Luneraria.UI.Tests.StartScreenTests" classname="Luneraria.UI.Tests.StartScreenTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:46:15Z" end-time="2026-10-11 10:46:26Z" duration="10.696725" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1034" name="StartScreenLeadsIntoTheMatchAndBack" fullname="Luneraria.UI.Tests.StartScreenTests.StartScreenLeadsIntoTheMatchAndBack" methodname="StartScreenLeadsIntoTheMatchAndBack" classname="Luneraria.UI.Tests.StartScreenTests" runstate="Runnable" seed="1202486836" result="Passed" start-time="2026-10-11 10:46:15Z" end-time="2026-10-11 10:46:26Z" duration="10.693883" 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="1035" name="UiToolkitProbeTests" fullname="Luneraria.UI.Tests.UiToolkitProbeTests" classname="Luneraria.UI.Tests.UiToolkitProbeTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:46:26Z" duration="0.075115" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1036" name="PanelRendersIntoTexture" fullname="Luneraria.UI.Tests.UiToolkitProbeTests.PanelRendersIntoTexture" methodname="PanelRendersIntoTexture" classname="Luneraria.UI.Tests.UiToolkitProbeTests" runstate="Runnable" seed="1386425142" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:46:26Z" duration="0.074696" 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="1045" 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:46:26Z" end-time="2026-10-11 10:51:09Z" duration="282.776816" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties>
|
||||||
|
<property name="_PID" value="5112" />
|
||||||
|
<property name="_APPDOMAIN" value="Unity Child Domain" />
|
||||||
|
<property name="platform" value="PlayMode" />
|
||||||
|
<property name="EditorOnly" value="False" />
|
||||||
|
</properties>
|
||||||
|
<test-suite type="TestSuite" id="1046" name="Luneraria" fullname="Luneraria" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:51:09Z" duration="282.775733" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1047" name="View" fullname="Luneraria.View" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:51:09Z" duration="282.775374" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestSuite" id="1048" name="Tests" fullname="Luneraria.View.Tests" runstate="Runnable" testcasecount="2" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:51:09Z" duration="282.774981" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-suite type="TestFixture" id="1041" name="SceneFrameRateTests" fullname="Luneraria.View.Tests.SceneFrameRateTests" classname="Luneraria.View.Tests.SceneFrameRateTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:48:16Z" duration="110.405146" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1042" name="SceneMatchIsTheSameAtRealFrameRates" fullname="Luneraria.View.Tests.SceneFrameRateTests.SceneMatchIsTheSameAtRealFrameRates" methodname="SceneMatchIsTheSameAtRealFrameRates" classname="Luneraria.View.Tests.SceneFrameRateTests" runstate="Runnable" seed="34224576" result="Passed" start-time="2026-10-11 10:46:26Z" end-time="2026-10-11 10:48:16Z" duration="110.403463" 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: 3222 frames in 26.9 s = 119.8 fps, up to 11 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
LUNE_SCENE_FPS unlimited: 85192 frames in 26.8 s = 3176.4 fps, up to 7 steps per frame, hash DCE95C96556F3A1A, same=True
|
||||||
|
]]></output>
|
||||||
|
</test-case>
|
||||||
|
</test-suite>
|
||||||
|
<test-suite type="TestFixture" id="1043" name="VerticalFunctionTests" fullname="Luneraria.View.Tests.VerticalFunctionTests" classname="Luneraria.View.Tests.VerticalFunctionTests" runstate="Runnable" testcasecount="1" result="Passed" start-time="2026-10-11 10:48:16Z" end-time="2026-10-11 10:51:09Z" duration="172.369313" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
|
||||||
|
<properties />
|
||||||
|
<test-case id="1044" name="BotMatchesRunVisiblyAndMatchTheCore" fullname="Luneraria.View.Tests.VerticalFunctionTests.BotMatchesRunVisiblyAndMatchTheCore" methodname="BotMatchesRunVisiblyAndMatchTheCore" classname="Luneraria.View.Tests.VerticalFunctionTests" runstate="Runnable" seed="123585462" result="Passed" start-time="2026-10-11 10:48:16Z" end-time="2026-10-11 10:51:09Z" duration="172.364578" 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/s2/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.186, "measured_fps": 30.015, "max_steps_per_frame": 17, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "60 fps", "frames": 1626, "seconds": 27.096, "measured_fps": 60.008, "max_steps_per_frame": 10, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "120 fps", "frames": 3222, "seconds": 26.903, "measured_fps": 119.764, "max_steps_per_frame": 11, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true},
|
||||||
|
{"target": "unlimited", "frames": 85192, "seconds": 26.821, "measured_fps": 3176.355, "max_steps_per_frame": 7, "steps": 12690, "hash": "DCE95C96556F3A1A", "winner": 1, "same_as_core": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
16
docs/port/logs/s2/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": 48451, "fps": 2422.539, "frame_ms_avg": 0.413, "frame_ms_p95": 0.42, "simulation_ms_per_frame": 0.004, "views_ms_per_frame": 0.134, "units_alive_max": 116}}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
docs/port/screenshots/s2/ablauf/01-runde1-aufmarsch.jpg
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
docs/port/screenshots/s2/ablauf/02-runde1-mid-kampf.jpg
Normal file
|
After Width: | Height: | Size: 600 KiB |
BIN
docs/port/screenshots/s2/ablauf/03-turm-faellt.jpg
Normal file
|
After Width: | Height: | Size: 536 KiB |
BIN
docs/port/screenshots/s2/ablauf/04-turm-ruine.jpg
Normal file
|
After Width: | Height: | Size: 528 KiB |
BIN
docs/port/screenshots/s2/ablauf/05-grotte-bosskampf.jpg
Normal file
|
After Width: | Height: | Size: 598 KiB |
BIN
docs/port/screenshots/s2/ablauf/06-held-kaempft.jpg
Normal file
|
After Width: | Height: | Size: 615 KiB |
BIN
docs/port/screenshots/s2/ablauf/07-boss-auf-der-lane.jpg
Normal file
|
After Width: | Height: | Size: 566 KiB |
BIN
docs/port/screenshots/s2/ablauf/08-ancient-faellt.jpg
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/port/screenshots/s2/ablauf/09-ancient-ruine.jpg
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
docs/port/screenshots/s2/ablauf/10-ende-uebersicht.jpg
Normal file
|
After Width: | Height: | Size: 426 KiB |
BIN
docs/port/screenshots/s2/ablauf/11-welle21-uebersicht.jpg
Normal file
|
After Width: | Height: | Size: 580 KiB |
BIN
docs/port/screenshots/s2/ablauf/12-welle21-kampf.jpg
Normal file
|
After Width: | Height: | Size: 638 KiB |
BIN
docs/port/screenshots/s2/ablauf/13-ancient-rot-faellt.jpg
Normal file
|
After Width: | Height: | Size: 591 KiB |
BIN
docs/port/screenshots/s2/ablauf/14-welle-ende.jpg
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
docs/port/screenshots/s2/spiel/01-team-aktionen.jpg
Normal file
|
After Width: | Height: | Size: 727 KiB |
BIN
docs/port/screenshots/s2/spiel/02-ancient-tooltip.jpg
Normal file
|
After Width: | Height: | Size: 736 KiB |
BIN
docs/port/screenshots/s2/spiel/03-rezept-im-shop.jpg
Normal file
|
After Width: | Height: | Size: 626 KiB |
BIN
docs/port/screenshots/s2/spiel/04-bollwerk-geschmiedet.jpg
Normal file
|
After Width: | Height: | Size: 612 KiB |
BIN
docs/port/screenshots/s2/spiel/05-glyphe-am-turm.jpg
Normal file
|
After Width: | Height: | Size: 706 KiB |
|
|
@ -79,11 +79,11 @@ public static class Program
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static int RulesComparison(int seeds)
|
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");
|
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; glyphs fired; Ancient levels; recipe items");
|
||||||
foreach (bool extended in new[] { false, true })
|
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;
|
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;
|
long income = 0; int barracks = 0, exhausted = 0, buybacks = 0, blessings = 0, sylvaraSeats = 0, sylvaraWins = 0, glyphs = 0, cores = 0, recipes = 0;
|
||||||
for (int seed = 1; seed <= seeds; seed++)
|
for (int seed = 1; seed <= seeds; seed++)
|
||||||
{
|
{
|
||||||
var match = new LuneMatch();
|
var match = new LuneMatch();
|
||||||
|
|
@ -103,6 +103,7 @@ public static class Program
|
||||||
if (extended && match.BarracksBroken(team, lane)) barracks++;
|
if (extended && match.BarracksBroken(team, lane)) barracks++;
|
||||||
match.BeginCombat();
|
match.BeginCombat();
|
||||||
match.FinishCombat();
|
match.FinishCombat();
|
||||||
|
glyphs += (match.Combat.GlyphUsed[0] ? 1 : 0) + (match.Combat.GlyphUsed[1] ? 1 : 0);
|
||||||
foreach (Player player in match.Players)
|
foreach (Player player in match.Players)
|
||||||
if (player.Hero != null && player.Hero.Exhausted) exhausted++;
|
if (player.Hero != null && player.Hero.Exhausted) exhausted++;
|
||||||
if (firstTower < 0 && match.LastSettlement != null && match.LastSettlement.BuildingsLost.Count > 0)
|
if (firstTower < 0 && match.LastSettlement != null && match.LastSettlement.BuildingsLost.Count > 0)
|
||||||
|
|
@ -121,10 +122,13 @@ public static class Program
|
||||||
if (player.Hero != null && player.Hero.HeroId == "held_sylvara") { sylvaraSeats++; if (match.Winner == player.Team) sylvaraWins++; }
|
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];
|
blessings += match.Boss.Blessing[0] + match.Boss.Blessing[1];
|
||||||
|
cores += match.Boss.CoreLevel[0] + match.Boss.CoreLevel[1];
|
||||||
|
foreach (Player player in match.Players)
|
||||||
|
if (player.Hero != null) recipes += player.Hero.Items.Count(id => ContentData.Items[id].IsRecipe);
|
||||||
}
|
}
|
||||||
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}",
|
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}; {18}; {19}; {20}",
|
||||||
extended ? "erweitert" : "referenz", seeds, blue, red, draws, open, (double)rounds / seeds, min, max, firstTowerWins, firstTowerGames,
|
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));
|
(double)income / (seeds * 6), barracks, exhausted, buybacks, blessings, sylvaraSeats, sylvaraWins, glyphs, cores, recipes));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||