Skip to content
Merged
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
1 change: 1 addition & 0 deletions .data/gamevals/synth.rscm
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,4 @@ crystal_sing=6234
toa_shadow_weapon_cast_fire_01=6410
shooting_star_whisle=4924
shooting_star_crash=4925
mm2_gorilla_boulder_impact=1444
7 changes: 7 additions & 0 deletions .data/gamevals/varn.rscm
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ generic_state_2=2
attacking_player=3
lastattack=4
flat_armour=5
immune_melee=6
immune_ranged=7
immune_magic=8
gorilla_protect_damage=9
gorilla_protect_hits=10
gorilla_miss_streak=11
gorilla_protect_switching=12
1 change: 1 addition & 0 deletions .data/gamevals/varp.rscm
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,4 @@ kc_smoke_devil=65615
kc_hydra=65616
kc_thermonuclear_smoke_devil=65654
kc_superior_monster=65655
kc_demonic_gorilla=65656
46 changes: 46 additions & 0 deletions .data/raw-cache/server/npcs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3727,6 +3727,52 @@ moveRestrict = "NoMove"
wanderRange = 0

[[npc]]
id = "npc.mm2_demon_gorilla_1_melee"
inherit = "npc.mm2_demon_gorilla_1_melee"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false

[[npc]]
id = "npc.mm2_demon_gorilla_1_ranged"
inherit = "npc.mm2_demon_gorilla_1_ranged"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false

[[npc]]
id = "npc.mm2_demon_gorilla_1_magic"
inherit = "npc.mm2_demon_gorilla_1_magic"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false

[[npc]]
id = "npc.mm2_demon_gorilla_2_melee"
inherit = "npc.mm2_demon_gorilla_2_melee"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false

[[npc]]
id = "npc.mm2_demon_gorilla_2_ranged"
inherit = "npc.mm2_demon_gorilla_2_ranged"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false

[[npc]]
id = "npc.mm2_demon_gorilla_2_magic"
inherit = "npc.mm2_demon_gorilla_2_magic"
[npc.params]
"param.death_anim"="seq.demonic_gorilla_death"
"param.killcount_varp"="varp.kc_demonic_gorilla"
"param.killcount_notify"=false
id = "npc.0_43_51_saltfish"
inherit = "npc.0_43_51_saltfish"
contentGroup = "content.net_bait_fishing_spot"
Expand Down
21 changes: 21 additions & 0 deletions .data/raw-cache/server/varn.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,24 @@ id = "varn.lastattack"

[[varn]]
id = "varn.flat_armour"

[[varn]]
id = "varn.immune_melee"

[[varn]]
id = "varn.immune_ranged"

[[varn]]
id = "varn.immune_magic"

[[varn]]
id = "varn.gorilla_protect_damage"

[[varn]]
id = "varn.gorilla_protect_hits"

[[varn]]
id = "varn.gorilla_miss_streak"

[[varn]]
id = "varn.gorilla_protect_switching"
6 changes: 6 additions & 0 deletions .data/raw-cache/server/varp.toml
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,12 @@ id = "varp.kc_superior_monster"
scope = "Perm"
transmit = "Never"

[[varp]]
isServerOnly = true
id = "varp.kc_demonic_gorilla"
scope = "Perm"
transmit = "Never"

[[varp]]
isServerOnly = true
id = "varp.drew_sandstone"
Expand Down
1 change: 1 addition & 0 deletions api/bosses/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ dependencies {
implementation(libs.guice)
implementation(projects.api.pluginCommons)
implementation(projects.api.combat.combatCommons)
implementation(projects.api.combat.combatFormulas)
implementation(projects.api.npc)
implementation(projects.api.player)
implementation(projects.api.random)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.rsmod.api.bosses.dsl

import dev.openrune.types.NpcMode
import org.rsmod.api.bosses.spec.*
import org.rsmod.api.combat.commons.types.MeleeAttackType as EngineMeleeAttackType

fun anim(seq: String, delay: Int = 0): Effect = Effect.Anim(seq, delay)

Expand Down Expand Up @@ -185,6 +186,7 @@ typealias Rotation = Selector.Rotation
typealias Run = Effect.Run
typealias TransitionTo = Effect.TransitionTo
typealias AllInRadius = TargetExpr.AllInRadius
typealias MeleeAttackType = EngineMeleeAttackType

val OnDeath: Condition = Condition.OnDeath
val OnSpawn: Condition = Condition.OnSpawn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package org.rsmod.api.bosses.runtime

import jakarta.inject.Inject
import jakarta.inject.Singleton
import org.rsmod.api.combat.formulas.AccuracyFormulae
import org.rsmod.api.player.hit.modifier.PlayerHitModifier
import org.rsmod.api.random.GameRandom
import org.rsmod.api.repo.npc.NpcRepository
import org.rsmod.api.repo.world.WorldRepository
Expand All @@ -21,4 +23,6 @@ class BossDeps @Inject constructor(
val collision: CollisionFlagMap,
val encounterRegistry: EncounterRegistry,
val extensionRegistry: BossExtensionRegistry,
val accuracy: AccuracyFormulae,
val playerHitModifier: PlayerHitModifier,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.rsmod.api.bosses.runtime

Check failure on line 1 in api/bosses/src/main/kotlin/org/rsmod/api/bosses/runtime/EffectInterpreter.kt

View workflow job for this annotation

GitHub Actions / spotless (advisory)

Not formatted. Run './gradlew spotlessApply' and commit the result.

import dev.openrune.ServerCacheManager
import dev.openrune.rscm.RSCM.asRSCM
Expand All @@ -10,8 +10,9 @@
import org.rsmod.api.bosses.spec.HitType as BossHitType
import org.rsmod.api.combat.commons.CombatEffects
import org.rsmod.api.combat.commons.DragonfireProtection
import org.rsmod.api.combat.commons.player.finishNpcHit
import org.rsmod.api.combat.commons.types.MeleeAttackType
import org.rsmod.api.npc.access.StandardNpcAccess
import org.rsmod.api.player.hit.queueHit
import org.rsmod.api.player.output.mes
import org.rsmod.api.player.stat.hitpoints
import org.rsmod.game.entity.Npc
Expand Down Expand Up @@ -118,13 +119,15 @@
}
val delay = hit.delay.coerceAtLeast(1)
for (t in targets) {
var damage = evaluateDamage(hit.damage)
var damage = evaluateDamage(hit.damage, hit.type, t)
dragonfireType(hit.type)?.let { dfType ->
val cap = DragonfireProtection.resolveMaxHit(t, dfType, damageMax(hit.damage))
val cap = DragonfireProtection.resolveMaxHit(t, dfType, damageMax(hit.damage, hit.type, t))
damage = if (cap <= 0) 0 else deps.random.of(cap + 1)
}
hit.spotanim?.let { t.spotanim(it, height = hit.spotanimHeight) }
t.queueHit(npc, delay, hit.type.toEngine(), damage)
if (damage > 0) {
hit.spotanim?.let { t.spotanim(it, height = hit.spotanimHeight) }
}
t.finishNpcHit(npc, delay, hit.type.toEngine(), damage, deps.playerHitModifier)
}
}

Expand Down Expand Up @@ -152,12 +155,17 @@
deps.worldRepo.projAnim(projAnim)

proj.hit?.let { hit ->
var damage = evaluateDamage(hit.damage)
var damage = evaluateDamage(hit.damage, hit.type, t)
dragonfireType(hit.type)?.let { dfType ->
val cap = DragonfireProtection.resolveMaxHit(t, dfType, damageMax(hit.damage))
val cap = DragonfireProtection.resolveMaxHit(t, dfType, damageMax(hit.damage, hit.type, t))
damage = if (cap <= 0) 0 else deps.random.of(cap + 1)
}
t.queueHit(npc, projAnim.serverCycles, hit.type.toEngine(), damage)
if (damage > 0) {
hit.spotanim?.let {
t.spotanim(it, delay = projAnim.clientCycles, height = hit.spotanimHeight)
}
}
t.finishNpcHit(npc, projAnim.serverCycles, hit.type.toEngine(), damage, deps.playerHitModifier)
}
}

Expand All @@ -168,8 +176,8 @@
it.coords.chebyshevDistance(center) <= radius
}
for (t in targets) {
val damage = evaluateDamage(aoe.damage)
t.queueHit(npc, 0, aoe.type.toEngine(), damage)
val damage = evaluateDamage(aoe.damage, aoe.type, t)
t.finishNpcHit(npc, 0, aoe.type.toEngine(), damage, deps.playerHitModifier)
}
}

Expand Down Expand Up @@ -203,7 +211,8 @@
deps.playerList.filter { it.coords.chebyshevDistance(center) <= effect.targetRadius }
for (player in landing) {
if (player.hitpoints > 0 && player.coords in tileSet) {
player.queueHit(npc, 1, effect.type.toEngine(), evaluateDamage(effect.damage))
val damage = evaluateDamage(effect.damage, effect.type, player)
player.finishNpcHit(npc, 1, effect.type.toEngine(), damage, deps.playerHitModifier)
}
}
}
Expand Down Expand Up @@ -307,11 +316,11 @@
}
}

private fun damageMax(expr: DamageExpr): Int =
private fun damageMax(expr: DamageExpr, hitType: BossHitType, t: Player): Int =
when (expr) {
is DamageExpr.Roll -> if (expr.range.isEmpty()) 0 else expr.range.last
is DamageExpr.Fixed -> expr.value
else -> evaluateDamage(expr)
else -> evaluateDamage(expr, hitType, t)
}

private fun dragonfireType(t: BossHitType): DragonfireProtection.DragonfireType? =
Expand All @@ -322,21 +331,39 @@
else -> null
}

private fun evaluateDamage(expr: DamageExpr): Int {
private fun evaluateDamage(expr: DamageExpr, hitType: BossHitType, t: Player): Int {
return when (expr) {
is DamageExpr.Fixed -> expr.value
is DamageExpr.Roll ->
if (expr.range.isEmpty()) 0
else {
expr.range.first + deps.random.of(expr.range.last - expr.range.first + 1)
}
is DamageExpr.Accuracy -> evaluateDamage(expr.on)
is DamageExpr.PercentOfTargetHp -> (target.hitpoints * expr.fraction).toInt()
is DamageExpr.Min -> minOf(evaluateDamage(expr.a), evaluateDamage(expr.b))
is DamageExpr.Max -> maxOf(evaluateDamage(expr.a), evaluateDamage(expr.b))
is DamageExpr.Accuracy -> {
val landed = rollAccuracy(hitType, t, expr.meleeAttackType)
evaluateDamage(if (landed) expr.on else expr.miss, hitType, t)
}
is DamageExpr.PercentOfTargetHp -> (t.hitpoints * expr.fraction).toInt()
is DamageExpr.Min -> minOf(evaluateDamage(expr.a, hitType, t), evaluateDamage(expr.b, hitType, t))
is DamageExpr.Max -> maxOf(evaluateDamage(expr.a, hitType, t), evaluateDamage(expr.b, hitType, t))
}
}

private fun rollAccuracy(
hitType: BossHitType,
t: Player,
meleeAttackType: MeleeAttackType? = null,
): Boolean =
when (hitType) {
BossHitType.Melee -> deps.accuracy.rollMeleeAccuracy(npc, t, meleeAttackType, deps.random)
BossHitType.Ranged -> deps.accuracy.rollRangedAccuracy(npc, t, deps.random)
BossHitType.Magic,
BossHitType.Dragonfire,
BossHitType.DragonfireMetal,
BossHitType.WyvernIce -> deps.accuracy.rollMagicAccuracy(npc, t, deps.random)
BossHitType.Typeless -> true
}

private fun BossHitType.toEngine(): HitType = when (this) {
BossHitType.Melee -> HitType.Melee
BossHitType.Ranged -> HitType.Ranged
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package org.rsmod.api.bosses.spec

import org.rsmod.api.combat.commons.types.MeleeAttackType

sealed interface DamageExpr {
data class Fixed(val value: Int) : DamageExpr
data class Roll(val range: IntRange) : DamageExpr
data class Accuracy(val on: DamageExpr, val miss: DamageExpr = Fixed(0)) : DamageExpr

data class Accuracy(
val on: DamageExpr,
val miss: DamageExpr = Fixed(0),
val meleeAttackType: MeleeAttackType? = null,
) : DamageExpr
data class PercentOfTargetHp(val fraction: Double) : DamageExpr
data class Min(val a: DamageExpr, val b: DamageExpr) : DamageExpr
data class Max(val a: DamageExpr, val b: DamageExpr) : DamageExpr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import dev.openrune.ServerCacheManager
import dev.openrune.rscm.RSCM
import dev.openrune.rscm.RSCMType
import dev.openrune.types.ItemServerType
import dev.openrune.types.SequenceServerType
import kotlin.math.min
import org.rsmod.api.config.refs.params
import org.rsmod.api.player.hit.modifier.PlayerHitModifier
import org.rsmod.api.player.hit.queueHit
import org.rsmod.api.player.lefthand
import org.rsmod.api.player.protect.ProtectedAccess
import org.rsmod.api.player.righthand
Expand All @@ -15,6 +16,8 @@ import org.rsmod.game.entity.Npc
import org.rsmod.game.entity.Player
import org.rsmod.game.entity.npc.NpcUid
import org.rsmod.game.entity.player.PlayerUid
import org.rsmod.game.hit.Hit
import org.rsmod.game.hit.HitType
import org.rsmod.game.type.getOrNull

private val ProtectedAccess.autoRetaliateDisabled by boolVarp("varp.option_nodef")
Expand Down Expand Up @@ -54,22 +57,33 @@ public fun ProtectedAccess.combatRetaliate(uid: PlayerUid, flinchDelay: Int) {
opPlayer2(source)
}

public fun Player.finishNpcHit(
source: Npc,
delay: Int,
type: HitType,
damage: Int,
modifier: PlayerHitModifier,
): Hit {
queueCombatRetaliate(source)
val hit = queueHit(source, delay, type, damage, modifier)
combatPlayDefendAnim()
return hit
}

public fun Player.combatPlayDefendAnim(clientDelay: Int = 0) {
val righthandType = getOrNull(righthand)
val lefthandType = getOrNull(lefthand)
val defendAnim = resolveDefendAnim(righthandType, lefthandType)
anim(defendAnim, delay = clientDelay)
}

private fun resolveDefendAnim(
righthand: ItemServerType?,
lefthand: ItemServerType?,
): String {
private fun resolveDefendAnim(righthand: ItemServerType?, lefthand: ItemServerType?): String {
val righthandAnim = righthand?.param(params.defend_anim)
val lefthandAnim = lefthand?.param(params.defend_anim)
return when {
lefthandAnim != null && !lefthandAnim.isType("seq.human_unarmedblock") -> RSCM.getReverseMapping(RSCMType.SEQ,lefthandAnim.id)
righthandAnim != null -> RSCM.getReverseMapping(RSCMType.SEQ,righthandAnim.id)
lefthandAnim != null && !lefthandAnim.isType("seq.human_unarmedblock") ->
RSCM.getReverseMapping(RSCMType.SEQ, lefthandAnim.id)
righthandAnim != null -> RSCM.getReverseMapping(RSCMType.SEQ, righthandAnim.id)
else -> "seq.human_unarmedblock"
}
}
Expand Down
Loading
Loading