public SimulationResult StartSimulation(Random rng, int id, Queue<Note> pattern=null)
{
var result = new SimulationResult(id);
if (SongData == null)
return result;
int totalScore = 0;
double scorePerNote = (TotalAppeal * LevelCoefficients[SongData.Level]) / SongData.Notes;
int totalLife = Life, maxLife = Life;
CenterEffect.SkillTriggerProbabilityUp skillRateUp = null;
if (Unit.Center != null && Unit.Center.CenterEffect != null && Unit.Center.CenterEffect is CenterEffect.SkillTriggerProbabilityUp)
{
skillRateUp = Unit.Center.CenterEffect as CenterEffect.SkillTriggerProbabilityUp;
}
double comboRate = 1;
List<TriggeredSkill> scoreUp = new List<TriggeredSkill>(),
comboBonus = new List<TriggeredSkill>(), overload = new List<TriggeredSkill>(),
damgeGuard = new List<TriggeredSkill>(), revival = new List<TriggeredSkill>();
if (pattern == null)
{
var interval = (double)SongData.Duration / SongData.Notes;
pattern = new Queue<Note>(Enumerable.Range(1, SongData.Notes).Select(x => new Note { Id = x, Time = x * interval }));
}
double currentTime = .01;
int notes=0;
bool sync = false;
while (currentTime <= SongData.Duration)
{
CheckSkillDueTime(currentTime, scoreUp, comboBonus, overload, damgeGuard, revival);
if (currentTime < SongData.Duration && !sync)
{
var f = (int)Math.Round(currentTime * TimeScale);
foreach (var slot in Unit.Slots)
{
if (slot != null && slot.Skill != null)
{
var sb = slot.Skill as Skill;
if (f % (sb.Interval*TimeScale) == 0)
{
//var propability = sb.EstimateProbability(slot.SkillLevel) * //素の確率
// (1 + (Song.Type.HasFlag(slot.Category) ? 0.3 : 0) + //属性一致ボーナス
// (skillRateUp != null && skillRateUp.Targets.HasFlag(slot.Category) ? skillRateUp.Rate : 0)); //センター効果
var propability = GetSkillTriggerProbability(slot, Unit.Center, Guest, Song);
if (SkillControl != SkillTriggerControl.NeverTrigger &&
(SkillControl == SkillTriggerControl.AlwaysTrigger || rng.NextDouble() < propability))
{
var skill = new TriggeredSkill
{
Who = slot,
Since = currentTime,
Until = currentTime + sb.EstimateDuration(slot.SkillLevel)
};
switch (sb.GetType().Name)
{
case nameof(Skill.DamageGuard):
damgeGuard.Add(skill);
break;
case nameof(Skill.Revival):
revival.Add(skill);
break;
case nameof(Skill.ScoreBonus):
scoreUp.Add(skill);
break;
case nameof(Skill.ComboBonus):
comboBonus.Add(skill);
break;
case nameof(Skill.Overload):
var o = sb as Skill.Overload;
if (totalLife - o.ConsumingLife > 0)
{
if (!damgeGuard.Any())
{
totalLife -= o.ConsumingLife;
}
overload.Add(skill);
}
else
{
continue;
}
break;
default:
break;
}
result.TriggeredSkills.Add(skill);
}
}
}
}
}
if (pattern.Count > 0 && pattern.Peek().Time <= currentTime)
{
comboRate = CalculateComboRate(notes, SongData.Notes);
totalLife += revival.Select(x => x.Who.Skill).Cast<Skill.Revival>().Select(x => x.Amount).DefaultIfEmpty(0).Max();
if (totalLife > maxLife) totalLife = maxLife;
var scoreUpRate = 1 + scoreUp.Select(x => x.Rate).Concat(overload.Select(x => x.Rate)).DefaultIfEmpty(0).Max();
var comboUpRate = 1 + comboBonus.Select(x => x.Rate).DefaultIfEmpty(0).Max();
totalScore += (int)Math.Round(scorePerNote * comboRate * scoreUpRate * comboUpRate);
notes++;
var note = pattern.Dequeue();
if (pattern.Count > 0 && note.Time == pattern.Peek().Time)
{
sync = true;
continue;
}
sync = false;
}
currentTime += 0.01;
}
result.Score = totalScore;
result.Duration = SongData.Duration;
result.RemainingLife = totalLife;
result.ScorePerNote = (int)Math.Round((double)totalScore / SongData.Notes);
ResultsUpToDate = true;
return result;
}