public RotationViewer(CalculationOptionsTankDK opts, Character ch) { InitializeComponent(); this.calcOpts = opts; this.rotation = calcOpts.m_Rotation; this.character = ch.Clone(); talents = character.DeathKnightTalents; txtBS.KeyUp += new KeyEventHandler(txtBS_KeyUp); txtDC.KeyUp += new KeyEventHandler(txtDC_KeyUp); txtDuration.KeyUp += new KeyEventHandler(txtDuration_KeyUp); txtFS.KeyUp += new KeyEventHandler(txtFS_KeyUp); txtHB.KeyUp += new KeyEventHandler(txtHB_KeyUp); txtHS.KeyUp += new KeyEventHandler(txtHS_KeyUp); txtIT.KeyUp += new KeyEventHandler(txtIT_KeyUp); txtNumDisease.KeyUp += new KeyEventHandler(txtNumDisease_KeyUp); txtOblit.KeyUp += new KeyEventHandler(txtOblit_KeyUp); txtPS.KeyUp += new KeyEventHandler(txtPS_KeyUp); txtSS.KeyUp += new KeyEventHandler(txtSS_KeyUp); txtPest.KeyUp += new KeyEventHandler(txtPest_KeyUp); txtUptime.KeyUp += new KeyEventHandler(txtUptime_KeyUp); txtDS.KeyUp += new KeyEventHandler(txtDS_KeyUp); txtHoW.KeyUp += new KeyEventHandler(txtHoW_KeyUp); txtRS.KeyUp += new KeyEventHandler(txtRS_KeyUp); txtBB.KeyUp += new KeyEventHandler(txtBB_KeyUp); txtDnD.KeyUp += new KeyEventHandler(txtDnD_KeyUp); UpdateStrikes(); }
private void btnRotation_Click(object sender, EventArgs e) { CalculationOptionsTankDK calcOpts = Character.CalculationOptions as CalculationOptionsTankDK; RotationViewer RV = new RotationViewer(calcOpts, Character); RV.ShowDialog(); Character.OnCalculationsInvalidated(); }
public void LoadCalculationOptions() { _loadingCalculationOptions = true; if (Character.CalculationOptions == null) Character.CalculationOptions = new CalculationOptionsTankDK(); calcOpts = Character.CalculationOptions as CalculationOptionsTankDK; // Model Specific Code // _loadingCalculationOptions = false; }
public CombatTable2(Character c, Stats stats, CharacterCalculationsTankDK calcs, CalculationOptionsTankDK calcOpts) { if (calcOpts.bExperimental) { this.m_CState = new CombatState(); if (c != null) { if (c.DeathKnightTalents == null) { c.DeathKnightTalents = new DeathKnightTalents(); } this.m_CState.m_Talents = (DeathKnightTalents)c.DeathKnightTalents.Clone(); } this.m_CState.m_Stats = stats.Clone(); m_Calcs = calcs; m_Opts = calcOpts; this.m_CState.m_NumberOfTargets = (float)m_Opts.uNumberTargets; m_Rotation = calcOpts.m_Rotation; //TODO: Handle Expertise if (c.MainHand != null && c.MainHand.Item.Type != ItemType.None) { m_CState.MH = new Weapon(c.MainHand.Item, m_CState.m_Stats, m_Opts, 0); m_CState.OH = null; if (c.MainHand.Slot != ItemSlot.TwoHand) { if (c.OffHand != null && c.OffHand.Item.Type != ItemType.None) { m_CState.OH = new Weapon(c.OffHand.Item, this.m_CState.m_Stats, m_Opts, 0); } } } else { m_CState.MH = null; m_CState.OH = null; } // Checking the rotation: if (m_Rotation.IcyTouch == 0 && m_Rotation.PlagueStrike == 0 && m_Rotation.BloodStrike == 0) { // Then this is probably a null rotation, and // so let's build one? m_Rotation = new Rotation(this.m_CState.m_Talents); } BuildRotation(); // TODO: move this out of the constructor CompileRotation(m_Rotation); } }
public void LoadCalculationOptions() { _loadingCalculationOptions = true; if (Character.CalculationOptions == null) { Character.CalculationOptions = new CalculationOptionsTankDK(); } calcOpts = Character.CalculationOptions as CalculationOptionsTankDK; // Model Specific Code // _loadingCalculationOptions = false; }
public void WeaponConstructorTest() { // Weapon takes an item object. // Refactor idea is for it to inherit from Item. Item i = new Item("Weapon Test", ItemQuality.Common, ItemType.OneHandMace, 101, null, ItemSlot.MainHand, null, false, null, null, ItemSlot.None, ItemSlot.None, ItemSlot.None, 10, 20, ItemDamageType.Physical, 2.0f, null); Stats stats = new Stats(); stats.Stamina = 100; CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK(); // calcOpts.talents = new DeathKnightTalents(); // float expertise = 0F; // Weapon target = new Weapon(i, stats, calcOpts, expertise); // Assert.IsNotNull(target); // Assert.AreNotEqual(0, target.baseDamage, "basedamage"); // Assert.AreNotEqual(0, target.damage, "adjusted damage"); // Assert.AreNotEqual(0, target.DPS, "DPS"); }
/// <summary> /// /// </summary> /// <param name="i">The item that represents the weapon in use</param> /// <param name="stats">Character stats</param> /// <param name="calcOpts">The calculation Options.</param> /// <param name="expertise">Expertise by racial weapon specialization</param> public Weapon(Item i, Stats stats, CalculationOptionsTankDK calcOpts, float expertise) { if (stats == null || calcOpts == null || calcOpts.talents == null) { // Missing necessary pieces. return; } if (i == null) { i = new Item(); i.Speed = 2f; i.MinDamage = 0; i.MaxDamage = 0; } // Ensure that the weapon that does damage has some sort of speed associated. if ((i.MinDamage > 0 || i.MaxDamage > 0) && i.Speed == 0) { i.Speed = 2f; #if DEBUG throw new Exception("No Speed on Item."); #endif } effectiveExpertise = stats.Expertise; float fightDuration = calcOpts.FightLength * 60f; if (i == null) { return; } baseSpeed = i.Speed; baseDamage = (float)(i.MinDamage + i.MaxDamage) / 2f + stats.WeaponDamage; #region Attack Speed { // TODO: make sure we're not double counting haste. hastedSpeed = baseSpeed / (1f + (StatConversion.GetHasteFromRating(stats.HasteRating, CharacterClass.DeathKnight)) + stats.PhysicalHaste); hastedSpeed /= 1f + 0.05f * (float)calcOpts.talents.ImprovedIcyTalons; if (calcOpts.Bloodlust) { // TODO: Update this bloodlust value to be a special effect //float bloodlustUptime = (calcOpts.Bloodlust * 40f); //if (bloodlustUptime > fightDuration) bloodlustUptime = 1f; //else bloodlustUptime /= fightDuration; float numLust = fightDuration % 300f; // bloodlust changed in 3.0, can only have one every 5 minutes. float fullLustDur = (numLust - 1) * 300f + 40f; if (fightDuration < fullLustDur) // if the last lust doesn't go its full duration { float lastLustFraction = (fullLustDur - fightDuration) / 40f; numLust -= 1f; numLust += lastLustFraction; } float bloodlustUptime = (numLust * 40f) / fightDuration; hastedSpeed /= 1f + (0.3f * bloodlustUptime); } } #endregion #region Dodge { chanceDodged = StatConversion.WHITE_DODGE_CHANCE_CAP[calcOpts.TargetLevel - 80]; // Changing this to use the statconversion formula like Target Dodge%; chanceDodged -= StatConversion.GetDodgeParryReducFromExpertise(stats.Expertise); if (chanceDodged < 0f) { chanceDodged = 0f; } } #endregion #region White Damage { // To do, this operation needs to be moved out into it's own space. // There's no reason it should sit in the constructor only. // White damage per hit. Basic white hits are use elsewhere. damage = baseDamage + (stats.AttackPower / 14f) * baseSpeed; DPS = 0f; if (hastedSpeed > 0) { DPS = damage / hastedSpeed; } } #endregion }
public void TankDK_BuildAcceptance() { Rawr.TankDK.CalculationsTankDK CalcTankDK = new Rawr.TankDK.CalculationsTankDK(); CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK(); m_char.CalculationOptions = calcOpts; this.testContextInstance.BeginTimer("GetCalc"); CharacterCalculationsBase calcs = CalcTankDK.GetCharacterCalculations(m_char); calcs.GetCharacterDisplayCalculationValues(); this.testContextInstance.EndTimer("GetCalc"); }
public void TankDK_OverallCheck() { Rawr.TankDK.CalculationsTankDK CalcTankDK = new Rawr.TankDK.CalculationsTankDK(); CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK(); m_char.CalculationOptions = calcOpts; Item additionalItem = new Item("TestItem", ItemQuality.Common, ItemType.None, 102010, "", ItemSlot.Back, "", false, new Stats(), null, ItemSlot.None, ItemSlot.None, ItemSlot.None, 0, 0, ItemDamageType.Physical, 0, ""); CharacterCalculationsTankDK calcs = CalcTankDK.GetCharacterCalculations(m_char) as CharacterCalculationsTankDK; float OValueBase = calcs.OverallPoints; float[] SValueBase = calcs.SubPoints; // Setup the stats on what we want. additionalItem.Stats.Stamina = 5000; calcs = CalcTankDK.GetCharacterCalculations(m_char, additionalItem) as CharacterCalculationsTankDK; float OValueStam = calcs.OverallPoints; float[] SValueStam = calcs.SubPoints; additionalItem.Stats.Stamina = 0; Assert.IsTrue(OValueBase < OValueStam, "Stamina"); additionalItem.Stats.DodgeRating = 5000; calcs = CalcTankDK.GetCharacterCalculations(m_char, additionalItem) as CharacterCalculationsTankDK; float OValueDodge = calcs.OverallPoints; float[] SValueDodge = calcs.SubPoints; additionalItem.Stats.DodgeRating = 0; additionalItem.Stats.DodgeRating = 10000; calcs = CalcTankDK.GetCharacterCalculations(m_char, additionalItem) as CharacterCalculationsTankDK; float OValueDodge2 = calcs.OverallPoints; float[] SValueDodge2 = calcs.SubPoints; additionalItem.Stats.DodgeRating = 0; Assert.IsTrue(OValueDodge < OValueDodge2, "Dodge2"); Assert.IsTrue(SValueBase[0] < SValueDodge[0], "Dodge1"); // Due to inverse scaling between Burst & Avoidance, only check Mitigation improvement. additionalItem.Stats.ParryRating = 5000; calcs = CalcTankDK.GetCharacterCalculations(m_char, additionalItem) as CharacterCalculationsTankDK; float OValueParry = calcs.OverallPoints; float[] SValueParry = calcs.SubPoints; additionalItem.Stats.ParryRating = 0; Assert.IsTrue(SValueBase[0] < SValueParry[0], "Parry"); // Due to inverse scaling between Burst & Avoidance, only check Mitigation improvement. additionalItem.Stats.MasteryRating = 5000; calcs = CalcTankDK.GetCharacterCalculations(m_char, additionalItem) as CharacterCalculationsTankDK; float OValueMastery = calcs.OverallPoints; float[] SValueMastery = calcs.SubPoints; additionalItem.Stats.MasteryRating = 0; Assert.IsTrue(OValueBase < OValueMastery, "Mastery"); }
public void TankDK_Rotation() { Rawr.TankDK.CharacterCalculationsTankDK CalcTankDK = new Rawr.TankDK.CharacterCalculationsTankDK(); CalculationOptionsTankDK calcOpts = new CalculationOptionsTankDK(); Rawr.DK.StatsDK TotalStats = new Rawr.DK.StatsDK(); Rawr.DPSDK.CharacterCalculationsDPSDK DPSCalcs = new Rawr.DPSDK.CharacterCalculationsDPSDK(); Rawr.DPSDK.CalculationOptionsDPSDK DPSopts = new Rawr.DPSDK.CalculationOptionsDPSDK(); Rawr.DK.DKCombatTable ct = new Rawr.DK.DKCombatTable(m_char, TotalStats, DPSCalcs, DPSopts, m_char.BossOptions); Rawr.DK.Rotation rot = new Rawr.DK.Rotation(ct, false); rot.PRE_BloodDiseased(); Assert.IsTrue(rot.m_TPS > 0, "rotation BloodDiseased produces 0 DPS"); }
public Stats getSpecialEffects(CalculationOptionsTankDK calcOpts, SpecialEffect effect) { Stats statsAverage = new Stats(); Rotation rRotation = calcOpts.m_Rotation; if (effect.Trigger == Trigger.Use) { if (calcOpts.bUseOnUseAbilities == true) { statsAverage.Accumulate(effect.GetAverageStats()); } } else { float trigger = 0f; float chance = effect.Chance; float duration = effect.Duration; float unhastedAttackSpeed = 2f; switch (effect.Trigger) { case Trigger.MeleeCrit: case Trigger.PhysicalCrit: trigger = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f); chance = combatTable.physCrits * effect.Chance; unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); break; case Trigger.MeleeHit: case Trigger.PhysicalHit: trigger = (1f / (rRotation.getMeleeSpecialsPerSecond() * (combatTable.m_bDW ? 2 : 1))) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f); chance = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)); unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); break; case Trigger.CurrentHandHit: case Trigger.MainHandHit: trigger = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.MH.hastedSpeed != 0 ? 1f / combatTable.MH.hastedSpeed : 0.5f); chance = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)); unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); break; case Trigger.OffHandHit: trigger = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.OH.hastedSpeed != 0 ? 1f / combatTable.OH.hastedSpeed : 0.5f); chance = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)); unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); break; case Trigger.DamageDone: case Trigger.DamageOrHealingDone: trigger = (1f / rRotation.getMeleeSpecialsPerSecond()) + (combatTable.combinedSwingTime != 0 ? 1f / combatTable.combinedSwingTime : 0.5f); unhastedAttackSpeed = (combatTable.MH != null ? combatTable.MH.baseSpeed : 2.0f); chance = effect.Chance * (1f - (combatTable.missedSpecial + combatTable.dodgedSpecial)); break; case Trigger.DamageSpellCast: case Trigger.SpellCast: case Trigger.DamageSpellHit: case Trigger.SpellHit: trigger = 1f / rRotation.getSpellSpecialsPerSecond(); chance = 1f - combatTable.spellResist; break; case Trigger.DamageSpellCrit: case Trigger.SpellCrit: trigger = 1f / rRotation.getSpellSpecialsPerSecond(); chance = combatTable.spellCrits * effect.Chance; break; case Trigger.DoTTick: trigger = (rRotation.BloodPlague + rRotation.FrostFever) / 3; break; case Trigger.DamageTaken: case Trigger.DamageTakenPhysical: trigger = calcOpts.BossAttackSpeed; chance *= 1f - (stats.Dodge + stats.Parry + stats.Miss); unhastedAttackSpeed = calcOpts.BossAttackSpeed; break; case Trigger.DamageTakenMagical: trigger = calcOpts.IncomingFromMagicFrequency; break; ////////////////////////////////// // DK specific triggers: case Trigger.BloodStrikeHit: case Trigger.HeartStrikeHit: trigger = rRotation.curRotationDuration / (rRotation.BloodStrike + rRotation.HeartStrike); break; case Trigger.PlagueStrikeHit: trigger = rRotation.curRotationDuration / rRotation.PlagueStrike; break; case Trigger.RuneStrikeHit: trigger = rRotation.curRotationDuration / rRotation.RuneStrike; break; case Trigger.IcyTouchHit: trigger = rRotation.curRotationDuration / rRotation.IcyTouch; break; case Trigger.DeathStrikeHit: trigger = rRotation.curRotationDuration / rRotation.DeathStrike; break; case Trigger.ObliterateHit: trigger = rRotation.curRotationDuration / rRotation.Obliterate; break; case Trigger.ScourgeStrikeHit: trigger = rRotation.curRotationDuration / rRotation.ScourgeStrike; break; case Trigger.FrostFeverHit: // Icy Talons triggers off this. trigger = rRotation.curRotationDuration / rRotation.IcyTouch; if (character.DeathKnightTalents.GlyphofHowlingBlast) { trigger += rRotation.curRotationDuration / rRotation.HowlingBlast; } break; } if (!float.IsInfinity(trigger) && !float.IsNaN(trigger)) { if (effect.UsesPPM()) { // If effect.chance < 0 , then it's using PPM. // Let's get the duration * how many times it procs per min: float UptimePerMin = 0; float fWeight = 0; if (duration == 0) // Duration of 0 means that it's a 1 time effect that procs every time the proc comes up. { fWeight = Math.Abs(effect.Chance) / 60; } else { UptimePerMin = duration * Math.Abs(effect.Chance); fWeight = UptimePerMin / 60; } statsAverage.Accumulate(effect.Stats, fWeight); } else { effect.AccumulateAverageStats(statsAverage, trigger, chance, unhastedAttackSpeed, calcOpts.FightLength * 60); } } } return(statsAverage); }