-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding Boomkin Capabilities #103
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package character.classes.druid.abilities | ||
|
||
/** | ||
* | ||
*/ | ||
class Innervate { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package character.classes.druid.abilities | ||
|
||
/** | ||
* | ||
*/ | ||
class InsectSwarm { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package character.classes.druid.abilities | ||
|
||
import character.Ability | ||
import character.Proc | ||
import character.classes.druid.debuff.MoonfireDot | ||
import character.classes.druid.talents.* | ||
import data.Constants | ||
import mechanics.General | ||
import mechanics.Spell | ||
import sim.Event | ||
import sim.EventResult | ||
import sim.EventType | ||
import sim.SimParticipant | ||
|
||
/** | ||
* | ||
*/ | ||
class Moonfire : Ability() { | ||
companion object { | ||
const val name = "Moonfire" | ||
} | ||
|
||
// TODO: lookup this actual value | ||
override val id: Int = 100 | ||
override val name: String = Companion.name | ||
override fun gcdMs(sp: SimParticipant): Int = sp.spellGcd().toInt() | ||
|
||
val baseCost = 495.0 | ||
override fun resourceCost(sp: SimParticipant): Double { | ||
val moonglow = sp.getTalent<Moonglow>(Moonglow.name) | ||
val costReductionPercent = moonglow?.reducedManaCostPercent() ?: 0.0 | ||
return General.resourceCostReduction(baseCost, listOf(costReductionPercent)) | ||
} | ||
|
||
val baseDamage = Pair(305.0, 357.0) | ||
val baseDot = 600.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably don't need this value in the main ability - it's covered in the DoT implementation |
||
val school = Constants.DamageType.ARCANE | ||
override fun cast(sp: SimParticipant) { | ||
val impMoonfire = sp.getTalent<ImprovedMoonfire>(ImprovedMoonfire.name) | ||
val impMoonfireIncreasedCritChance = impMoonfire?.increasedMoonfireCritChancePercent() ?: 0.0 | ||
val impMoonfireIncreasedDamagePercent = impMoonfire?.increasedMoonfireDamagePercent() ?: 0.0 | ||
|
||
val vengeance = sp.getTalent<Vengeance>(Vengeance.name) | ||
val increasedCritBonusDamage = vengeance?.increasedCritBonusDamagePercent() ?: 0.0 | ||
|
||
val moonfury = sp.getTalent<Moonfury>(Moonfury.name) | ||
val moonfuryBonusDamagePercent = moonfury?.increasedDamagePercent() ?: 0.0 | ||
|
||
val instantSpellPowerCoeff = 0.1495 | ||
|
||
val damageMulti = 1.0 + impMoonfireIncreasedDamagePercent + moonfuryBonusDamagePercent | ||
|
||
val damageRoll = Spell.baseDamageRoll(sp, baseDamage.first, baseDamage.second, school, instantSpellPowerCoeff) * damageMulti | ||
val damageResult = Spell.attackRoll( | ||
sp, | ||
damageRoll, | ||
school, | ||
isBinary = false, | ||
bonusCritChance = impMoonfireIncreasedCritChance, | ||
bonusCritMultiplier = 1.0 + increasedCritBonusDamage | ||
) | ||
|
||
val event = Event( | ||
eventType = EventType.DAMAGE, | ||
damageType = school, | ||
abilityName = name, | ||
amount = damageResult.first, | ||
result = damageResult.second | ||
) | ||
sp.logEvent(event) | ||
|
||
sp.sim.target.addDebuff(MoonfireDot(sp)) | ||
|
||
// Proc anything that can proc off non-periodic spell damage | ||
val triggerTypes = when(damageResult.second) { | ||
EventResult.HIT -> listOf(Proc.Trigger.SPELL_HIT, Proc.Trigger.ARCANE_DAMAGE) | ||
EventResult.CRIT -> listOf(Proc.Trigger.SPELL_CRIT, Proc.Trigger.ARCANE_DAMAGE) | ||
EventResult.RESIST -> listOf(Proc.Trigger.SPELL_RESIST) | ||
EventResult.PARTIAL_RESIST_HIT -> listOf(Proc.Trigger.SPELL_HIT, Proc.Trigger.ARCANE_DAMAGE) | ||
EventResult.PARTIAL_RESIST_CRIT -> listOf(Proc.Trigger.SPELL_CRIT, Proc.Trigger.ARCANE_DAMAGE) | ||
else -> null | ||
} | ||
|
||
if (triggerTypes != null) { | ||
sp.fireProc(triggerTypes, listOf(), this, event) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package character.classes.druid.abilities | ||
|
||
import character.Ability | ||
|
||
/** | ||
* | ||
*/ | ||
class Starfire { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package character.classes.druid.abilities | ||
|
||
/** | ||
* | ||
*/ | ||
class Wrath { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package character.classes.druid.debuff | ||
|
||
import character.Ability | ||
import character.Debuff | ||
import character.Proc | ||
import character.classes.druid.talents.ImprovedMoonfire | ||
import character.classes.druid.talents.Moonfury | ||
import data.Constants | ||
import data.itemsets.CorruptorRaiment | ||
import data.itemsets.VoidheartRaiment | ||
import mechanics.Spell | ||
import sim.Event | ||
import sim.EventResult | ||
import sim.EventType | ||
|
||
import sim.SimParticipant | ||
import kotlin.reflect.KProperty | ||
|
||
class MoonfireDot(owner: SimParticipant) : Debuff(owner) { | ||
companion object { | ||
const val name = "Moonfire (DoT)" | ||
} | ||
override val name: String = Companion.name | ||
override val durationMs: Int = 12000 | ||
override val tickDeltaMs: Int = 3000 | ||
|
||
val moonfireDot = object : Ability() { | ||
override val id: Int = 26988 | ||
override val name: String = Companion.name | ||
override fun gcdMs(sp: SimParticipant): Int = 0 | ||
|
||
val dmgPerTick = 150.0 | ||
val numTicks = durationMs / tickDeltaMs | ||
val school = Constants.DamageType.ARCANE | ||
override fun cast(sp: SimParticipant) { | ||
val spellPowerCoeff = 0.1302 | ||
|
||
val impMoonfire = sp.getTalent<ImprovedMoonfire>(ImprovedMoonfire.name) | ||
val impMoonfireIncreasedDamagePercent = impMoonfire?.increasedMoonfireDamagePercent() ?: 0.0 | ||
|
||
val moonfury = sp.getTalent<Moonfury>(Moonfury.name) | ||
val moonFuryIncreasedDamagePercent = moonfury?.increasedDamagePercent() ?: 0.0 | ||
|
||
val damageMulti = 1.0 + impMoonfireIncreasedDamagePercent + moonFuryIncreasedDamagePercent | ||
val damageRoll = Spell.baseDamageRollSingle(owner, dmgPerTick, school, spellPowerCoeff) * damageMulti | ||
|
||
val event = Event( | ||
eventType = EventType.DAMAGE, | ||
damageType = school, | ||
abilityName = name, | ||
amount = damageRoll, | ||
result = EventResult.HIT, | ||
) | ||
owner.logEvent(event) | ||
} | ||
} | ||
override fun tick(sp: SimParticipant) { | ||
moonfireDot.cast(sp) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package character.classes.druid.specs | ||
|
||
import character.Spec | ||
import character.SpecEpDelta | ||
|
||
/** | ||
*/ | ||
class Boomkin : Spec() { | ||
override val name: String = "Boomkin" | ||
override val epBaseStat: SpecEpDelta = spellPowerBase | ||
override val epStatDeltas: List<SpecEpDelta> = defaultCasterDeltas | ||
|
||
override fun redSocketEp(deltas: Map<String, Double>): Double { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't sure what the purpose of these were actually. I just copied it straight from the warlock destruction spec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are for EP calculation - each socket should have an implementation that reasonably estimates which stat would be put in that socket. I believe boomies would generally use spelldamage in every socket, so the yellow could probably use that as a basis as well. |
||
// 12 spell dmg | ||
return 12.0 | ||
} | ||
|
||
override fun yellowSocketEp(deltas: Map<String, Double>): Double { | ||
// 5 hit rating / 6 spell dmg | ||
return ((deltas["spellHitRating"] ?: 0.0) * 5.0) + 6.0 | ||
} | ||
|
||
override fun blueSocketEp(deltas: Map<String, Double>): Double { | ||
// 6 spell dmg | ||
return 6.0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package character.classes.druid.talents | ||
|
||
import character.Buff | ||
import character.Stats | ||
import character.Talent | ||
import mechanics.Rating | ||
import sim.SimParticipant | ||
|
||
/** | ||
* | ||
*/ | ||
class BalanceOfPower(currentRank: Int) : Talent(currentRank) { | ||
companion object { | ||
const val name = "Balance of Power" | ||
} | ||
|
||
override val name: String = Companion.name | ||
override val maxRank: Int = 2 | ||
|
||
val buff = object : Buff() { | ||
override val name: String = MoonkinForm.name | ||
override val durationMs: Int = -1 | ||
override val hidden: Boolean = true | ||
|
||
override fun modifyStats(sp: SimParticipant): Stats { | ||
// TODO: is it easier to do this than add the 2% hit on each spell call? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't sure how best to handle these types of things. It felt easier to just update the sims hit rating rather than do additional calculations per spell to add the additional hit percent. I was also trying to prevent confusion between using percents as 0 < pct < 1 and 0 < pct < 100 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It depends on the ability - many talents like this are spell-specific, so they would have to be limited. Since Balance of Power applies to all spells, implementing as generic hit rating is just fine! |
||
return Stats(spellHitRating= currentRank * 2 * Rating.spellHitPerPct) | ||
} | ||
} | ||
|
||
override fun buffs(sp: SimParticipant): List<Buff> = listOf(buff) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package character.classes.druid.talents | ||
|
||
import character.Buff | ||
import character.Stats | ||
import character.Talent | ||
import sim.SimParticipant | ||
|
||
/** | ||
* | ||
*/ | ||
class Dreamstate(currentRank: Int) : Talent(currentRank) { | ||
companion object { | ||
const val name = "Dreamstate" | ||
} | ||
|
||
override val name: String = Companion.name | ||
override val maxRank: Int = 3 | ||
|
||
val buff = object : Buff() { | ||
override val name: String = Companion.name | ||
override val durationMs: Int = -1 | ||
override val hidden: Boolean = true | ||
|
||
override fun modifyStats(sp: SimParticipant): Stats { | ||
val percentOfInt: Double = when(currentRank) { | ||
3 -> .1 | ||
2 -> .07 | ||
1 -> .04 | ||
else -> 0.0 | ||
} | ||
|
||
return Stats(manaPer5Seconds = (sp.intellect() * percentOfInt).toInt()) | ||
} | ||
} | ||
|
||
override fun buffs(sp: SimParticipant): List<Buff> = listOf(buff) | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package character.classes.druid.talents | ||
|
||
import character.Talent | ||
|
||
/** | ||
* | ||
*/ | ||
class FocusedStarlight(currentRank: Int) : Talent(currentRank) { | ||
companion object { | ||
const val name = "Focused Starlight" | ||
} | ||
|
||
override val name: String = Companion.name | ||
override val maxRank: Int = 2 | ||
|
||
fun increasedWrathCritChancePercent() : Double { | ||
return currentRank * .02 | ||
} | ||
|
||
fun increasedStarfireCritChancePercent() : Double { | ||
return increasedWrathCritChancePercent() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package character.classes.druid.talents | ||
|
||
/** | ||
* | ||
*/ | ||
class ForceOfNature { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package character.classes.druid.talents | ||
|
||
/** | ||
* | ||
*/ | ||
class ImprovedMarkOfTheWild { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package character.classes.druid.talents | ||
|
||
import character.Talent | ||
|
||
/** | ||
* | ||
*/ | ||
class ImprovedMoonfire(currentRank: Int) : Talent(currentRank) { | ||
companion object { | ||
const val name = "Improved Moonfire" | ||
} | ||
|
||
override val name: String = Companion.name | ||
override val maxRank: Int = 2 | ||
|
||
fun increasedMoonfireCritChancePercent() : Double { | ||
return 0.05 * currentRank | ||
} | ||
|
||
fun increasedMoonfireDamagePercent() : Double { | ||
return increasedMoonfireCritChancePercent() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package character.classes.druid.talents | ||
|
||
import character.Talent | ||
|
||
/** | ||
* | ||
*/ | ||
class InsectSwarm(currentRank: Int) : Talent(currentRank) { | ||
companion object { | ||
const val name = "Insect Swarm" | ||
} | ||
|
||
override val name: String = Companion.name | ||
override val maxRank: Int = 1 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rank 12 is
26988