Skip to content
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/commonMain/kotlin/character/Proc.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ abstract class Proc {
SHAMAN_CAST_STORMSTRIKE,
SHAMAN_CRIT_LIGHTNING_BOLT,

DRUID_CAST_STARFIRE,
DRUID_CAST_WRATH,

WARLOCK_CRIT_INCINERATE,
WARLOCK_CRIT_SHADOW_BOLT,
WARLOCK_HIT_INCINERATE,
Expand Down
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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rank 12 is 26988

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
Copy link
Owner

Choose a reason for hiding this comment

The 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)
}
}
27 changes: 27 additions & 0 deletions src/commonMain/kotlin/character/classes/druid/specs/Boomkin.kt
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 {
Copy link
Author

Choose a reason for hiding this comment

The 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.

Copy link
Owner

Choose a reason for hiding this comment

The 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?
Copy link
Author

Choose a reason for hiding this comment

The 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

Copy link
Owner

Choose a reason for hiding this comment

The 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
}
Loading