penance healer logic

This commit is contained in:
Tooze 2026-06-10 12:41:35 -04:00
parent bffefaab30
commit 5291b13ff7
3 changed files with 209 additions and 9 deletions

View file

@ -3,7 +3,9 @@ package content.minigame.barbassault
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.PenanceType
import core.api.getTimer
import core.api.hasTimerActive
import core.api.registerTimer
import core.api.removeTimer
import core.api.spawnTimer
import core.game.node.entity.Entity
import core.game.node.entity.combat.ImpactHandler
@ -265,3 +267,13 @@ fun applyBAPoison (entity: Entity, source: Entity, severity: Int) {
}
}
fun cureBAPoison(entity: Entity) {
if (!hasTimerActive<BAPoison>(entity)) {
return
}
removeTimer<BAPoison>(entity)
}
fun isBAPoisoned(entity: Entity): Boolean {
return hasTimerActive<BAPoison>(entity)
}

View file

@ -145,10 +145,17 @@ class BarbassaultArenaListener : InteractionListener {
Pulser.submit(object : Pulse(3, player) {
override fun pulse(): Boolean {
val restoreAmount = getHealerHealAmount(player)
val curedPoison = isBAPoisoned(player)
heal(player, restoreAmount)
cureBAPoison(player)
player.settings.updateRunEnergy((-restoreAmount).toDouble())
player.unlock()
sendMessage(player,"You've been healed $restoreAmount hitpoints.")
val message = if (curedPoison) {
"You've been healed $restoreAmount hitpoints and your poison cured."
} else {
"You've been healed $restoreAmount hitpoints."
}
sendMessage(player, message)
getBASession(player)?.updateBarbHealerIfaceHPOfPlayer(player)
return true
}
@ -172,7 +179,8 @@ class BarbassaultArenaListener : InteractionListener {
onUseWithPlayer(*All_HEALING_VIALS) { player, _, target ->
target as Player
//todo find Healing sound
if (target.skills.lifepoints >= target.skills.maximumLifepoints) {
val targetPoisoned = isBAPoisoned(target)
if (target.skills.lifepoints >= target.skills.maximumLifepoints && !targetPoisoned) {
sendMessage(player, "The player's hitpoints are full.")
return@onUseWithPlayer true
}
@ -188,10 +196,11 @@ class BarbassaultArenaListener : InteractionListener {
val restoreAmount =
minOf(getHealerHealAmount(player), target.skills.maximumLifepoints - target.skills.lifepoints)
sendMessage(target, "You've been healed $restoreAmount hitpoints.")
sendMessage(player, "You healed $restoreAmount hitpoints.")
val curedPoison = isBAPoisoned(target)
sendHealerVialMessages(player, target, restoreAmount, curedPoison)
player.animate(Animation(537))
heal(target, restoreAmount)
cureBAPoison(target)
target.settings.updateRunEnergy((-restoreAmount).toDouble())
session?.updateBarbHealerIfaceHPOfPlayer(target)
session?.eventBus?.emit(HitpointsHealed(player, restoreAmount))
@ -347,6 +356,24 @@ class BarbassaultArenaListener : InteractionListener {
val level = getBALevels(player).heal
return healerHealAmounts.getOrElse(level) { healerHealAmounts.last() }
}
private fun sendHealerVialMessages(player: Player, target: Player, restoreAmount: Int, curedPoison: Boolean) {
when {
restoreAmount > 0 && curedPoison -> {
sendMessage(target, "You've been healed $restoreAmount hitpoints and your poison cured.")
sendMessage(player, "You healed $restoreAmount hitpoints and cured their poison.")
}
restoreAmount > 0 -> {
sendMessage(target, "You've been healed $restoreAmount hitpoints.")
sendMessage(player, "You healed $restoreAmount hitpoints.")
}
curedPoison -> {
sendMessage(target, "Your poison has been cured.")
sendMessage(player, "You cured their poison.")
}
}
}
fun fillHealerVial(player: Player){
queueScript(player,1, QueueStrength.STRONG) {
if (player.inventory.containsAtLeastOneItem(All_HEALING_VIALS)) {
@ -478,4 +505,4 @@ Blocking off entrance, does NOT STOP runners entering, only slows
Announce in game room when All of a monster has been killed
" All of the $PENANCE Name have been killed!
* */
* */

View file

@ -1,15 +1,24 @@
package content.minigame.barbassault.arena.npcs
import content.minigame.barbassault.arena.BarbAssEvent
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.BA_SESSION_KEY
import content.minigame.barbassault.applyBAPoison
import content.minigame.barbassault.getBASession
import core.api.forceWalk
import core.api.hasLineOfSight
import core.game.node.entity.Entity
import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import core.game.node.entity.player.Player
import core.game.node.entity.skill.Skills
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.update.flag.context.Animation
import org.rs09.consts.NPCs
import kotlin.math.abs
val PENANCE_HEALER_IDS = intArrayOf(
//NPCs.PENANCE_HEALER_5043,//tut
@ -33,14 +42,29 @@ val PENANCE_HEALER_DEFINITIONS = listOf(
)
class PenanceHealerNPC : NPCBehavior(*PENANCE_HEALER_IDS) {
private enum class HealerMode { TARGET_PLAYER, TARGET_RUNNER, WANDER }
private companion object {
const val MODE = "ba-healer:mode"
const val TARGET = "ba-healer:target"
const val WANDER_TICKS = "ba-healer:wander-ticks"
const val WANDER_MOVE_TICKS = "ba-healer:wander-move-ticks"
const val VISION_RANGE = 15
const val WANDER_AFTER_ACTION_TICKS = 4
const val WANDER_STEP_TICKS = 5
const val PLAYER_POISON_DAMAGE = 2
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
override fun onCreation (self: NPC) {
self.setAttribute("barbass-type", PenanceType.HEALER)
self.isWalks = true
self.walkRadius = 24
self.setAttribute(MODE, HealerMode.TARGET_PLAYER)
self.isWalks = false
self.walkRadius = 0
self.isRespawn = false
self.properties.defenceAnimation = Animation(5105)
@ -59,10 +83,147 @@ class PenanceHealerNPC : NPCBehavior(*PENANCE_HEALER_IDS) {
}
//sendMessage(player,"that's the wrong type of poisoned food to use! Penalty!")
override fun tick(self: NPC): Boolean {
val session = self.getAttribute<BarbassaultSession>(BA_SESSION_KEY) ?: return false
return true
if (!hasLivingRunners(session)) {
wander(self)
self.shouldPreventStacking(self)
return false
}
val wanderTicks = self.getAttribute(WANDER_TICKS, 0)
if (wanderTicks > 0) {
self.setAttribute(WANDER_TICKS, wanderTicks - 1)
wander(self)
self.shouldPreventStacking(self)
return false
}
when (self.getAttribute(MODE, HealerMode.TARGET_PLAYER)) {
HealerMode.TARGET_PLAYER -> tickPlayerTarget(self, session)
HealerMode.TARGET_RUNNER -> tickRunnerTarget(self, session)
HealerMode.WANDER -> wander(self)
}
self.shouldPreventStacking(self)
return false
}
private fun tickPlayerTarget(self: NPC, session: BarbassaultSession) {
val target = currentTarget<Player>(self) ?: visiblePlayer(self, session)?.also { setTarget(self, it) }
if (target == null || !isValidPlayerTarget(self, target)) {
clearTarget(self)
return
}
if (!isCloseEnough(self, target)) {
forceWalk(self, target.location, "DUMB")
return
}
self.resetWalk()
self.face(target)
target.impactHandler.manualHit(self, PLAYER_POISON_DAMAGE, ImpactHandler.HitsplatType.POISON)
applyBAPoison(target, self, 6)
clearTarget(self)
self.setAttribute(MODE, HealerMode.TARGET_RUNNER)
self.setAttribute(WANDER_TICKS, WANDER_AFTER_ACTION_TICKS)
}
private fun tickRunnerTarget(self: NPC, session: BarbassaultSession) {
val target = currentTarget<NPC>(self) ?: visibleRunner(self, session)?.also { setTarget(self, it) }
if (target == null || !isValidRunnerTarget(self, target)) {
clearTarget(self)
return
}
if (!isCloseEnough(self, target)) {
forceWalk(self, target.location, "DUMB")
return
}
self.resetWalk()
self.face(target)
target.skills.heal(target.skills.maximumLifepoints)
clearTarget(self)
self.setAttribute(MODE, HealerMode.TARGET_PLAYER)
self.setAttribute(WANDER_TICKS, WANDER_AFTER_ACTION_TICKS)
}
private fun visiblePlayer(self: NPC, session: BarbassaultSession): Player? {
return session.team.allPlayers()
.filter { isValidPlayerTarget(self, it) }
.minByOrNull { it.location.getDistance(self.location) }
}
private fun visibleRunner(self: NPC, session: BarbassaultSession): NPC? {
return session.sessionNpcs
.filter { isValidRunnerTarget(self, it) }
.minByOrNull { it.location.getDistance(self.location) }
}
private fun isValidPlayerTarget(self: NPC, player: Player): Boolean {
return player.skills.lifepoints > 0 &&
tileRadius(self.location, player.location) <= VISION_RANGE &&
hasLineOfSight(self, player)
}
private fun isValidRunnerTarget(self: NPC, runner: NPC): Boolean {
return runner.getAttribute<PenanceType?>("barbass-type", null) == PenanceType.RUNNER &&
runner.skills.lifepoints > 0 &&
tileRadius(self.location, runner.location) <= VISION_RANGE &&
hasLineOfSight(self, runner)
}
private fun hasLivingRunners(session: BarbassaultSession): Boolean {
return session.sessionNpcs.any {
it.getAttribute<PenanceType?>("barbass-type", null) == PenanceType.RUNNER &&
it.skills.lifepoints > 0
}
}
private fun wander(self: NPC) {
val moveTicks = self.getAttribute(WANDER_MOVE_TICKS, 0) + 1
if (moveTicks < WANDER_STEP_TICKS) {
self.setAttribute(WANDER_MOVE_TICKS, moveTicks)
return
}
self.setAttribute(WANDER_MOVE_TICKS, 0)
val destination = randomWalkableTile(self.location) ?: return
forceWalk(self, destination, "DUMB")
}
private fun randomWalkableTile(location: Location): Location? {
val options = listOf(
location.transform(0, 1, 0),
location.transform(1, 0, 0),
location.transform(0, -1, 0),
location.transform(-1, 0, 0),
).filter { RegionManager.isTeleportPermitted(it) }
return options.randomOrNull()
}
private fun isCloseEnough(self: NPC, target: Entity): Boolean {
return self.location == target.location || self.location.isNextTo(target)
}
private fun tileRadius(a: Location, b: Location): Int {
return maxOf(abs(a.x - b.x), abs(a.y - b.y))
}
private fun setTarget(self: NPC, target: Entity) {
self.setAttribute(TARGET, target)
}
private inline fun <reified T : Entity> currentTarget(self: NPC): T? {
return self.getAttribute<Entity?>(TARGET, null) as? T
}
private fun clearTarget(self: NPC) {
self.removeAttribute(TARGET)
}
override fun onDeathFinished(self: NPC, killer: Entity) {
super.onDeathFinished(self, killer)
@ -93,4 +254,4 @@ healer_Idle_5104
healer_hit_5105
healer_Death_5106
Healer_attack_5107?
*/
*/