Merge branch 'master' into 'master'

Draft: Barb Assault: Progressing

See merge request 2009scape/2009scape!2115
This commit is contained in:
Tooze 2026-07-31 16:42:32 +00:00
commit 0c027bd77b
60 changed files with 7970 additions and 67 deletions

View file

@ -0,0 +1,128 @@
package content.minigame.barbassault
import core.api.*
import core.game.component.Component
import core.game.node.entity.player.Player
import org.rs09.consts.Components
data class RoleChildIds(
val chpId: Int,
val levelId: Int,
val rewardNextLevelId: Int,
val pointsNextLevelId: Int
)
data class RoleDisplayValues(
val chpId: Int,
val levelId: Int,
val rewardNextLevelId: Int,
val pointsNextLevelId: Int,
val rewardText: String,
val pointsText: String
)
data class RewardInfo(
val reward: String,
val pointsToNextLevel: String
)
class BarbassBlackBoard {
companion object {
const val PLAYER_NAME = 19
const val CURRENT_WAVE = 25
val roleComponents: Map<BarbRole, RoleChildIds> = mapOf(
BarbRole.ATTACKER to RoleChildIds(20,15,27,10),
BarbRole.DEFENDER to RoleChildIds(21,16,28,11),
BarbRole.COLLECTOR to RoleChildIds(22,17,29,12),
BarbRole.HEALER to RoleChildIds(23,18,30,13)
)
fun getRewardInfo(role: BarbRole, level: Int): RewardInfo {
val reward = when (role) {
BarbRole.ATTACKER -> when (level) {
1 -> "+2 bonus damage"
2 -> "+3 bonus damage"
3 -> "+4 bonus damage"
4 -> "+5 bonus damage"
else -> " - Mastered - "
}
BarbRole.DEFENDER -> when (level) {
1 -> "Lure range 5"
2 -> "Lure range 6"
3 -> "Lure range 8"
4 -> "Lure range 10"
else -> " - Mastered - "
}
//https://youtu.be/wb1InggbaZc?t=57
BarbRole.COLLECTOR -> when (level) {
1 -> "Egg convert success 20%"
2 -> "Egg convert success 40%"
3 -> "Egg convert success 60%"
4 -> "Egg convert success 80%"
else -> " - Mastered - "
}
BarbRole.HEALER -> when (level) {
1 -> "Heal 15 points"
2 -> "Heal 20 points"
3 -> "Heal 25 points"
4 -> "Heal 35 points"
else -> " - Mastered - "
}
}
val points = getPointsTillNextLevel(level)
return RewardInfo(reward, points)
}
fun getPointsTillNextLevel(level: Int): String {
return when (level) {
1 -> "200"
2 -> "300"
3 -> "400"
4 -> "500"
else -> "---"
}
}
fun getAllRoleValues(role: BarbRole, level: Int): RoleDisplayValues {
val ids = roleComponents[role]
val rewardInfo = getRewardInfo(role, level)
return RoleDisplayValues(
ids!!.chpId, ids.levelId, ids.rewardNextLevelId, ids.pointsNextLevelId,
rewardInfo.reward, rewardInfo.pointsToNextLevel
)
}
fun updateBlackBoard(player: Player) {
player.interfaceManager.open(Component(Components.BARBASSAULT_PLAYERSTAT_490))
setInterfaceText(player, player.username, Components.BARBASSAULT_PLAYERSTAT_490, PLAYER_NAME)
setInterfaceText(player, getBAWave(player).toString(), Components.BARBASSAULT_PLAYERSTAT_490, CURRENT_WAVE)
val playerLevel = getBALevels(player)
val playerPoints = getBAPoints(player)
for (role in BarbRole.values()) {
val level = when (role) {
BarbRole.ATTACKER -> playerLevel.atk
BarbRole.DEFENDER -> playerLevel.def
BarbRole.COLLECTOR -> playerLevel.col
BarbRole.HEALER -> playerLevel.heal
}
val points = when (role) {
BarbRole.ATTACKER -> playerPoints.atk
BarbRole.DEFENDER -> playerPoints.def
BarbRole.COLLECTOR -> playerPoints.col
BarbRole.HEALER -> playerPoints.heal
}
val roleValues = getAllRoleValues(role, level)
setInterfaceText(player, points.toString(), 490, roleValues.chpId)
setInterfaceText(player, level.toString(), 490, roleValues.levelId)
setInterfaceText(player, roleValues.rewardText, 490, roleValues.rewardNextLevelId)
setInterfaceText(player, roleValues.pointsText, 490, roleValues.pointsNextLevelId)
}
}
}
}

View file

@ -0,0 +1,228 @@
package content.minigame.barbassault
import content.minigame.barbassault.bots.oldbot.BABot
import content.minigame.barbassault.lobby.BALobby
import core.api.*
import core.game.container.impl.EquipmentContainer
import core.game.node.item.Item
import core.game.system.command.Privilege
import core.game.system.command.sets.CommandSet
import core.game.world.map.Location
import core.game.world.repository.Repository
import core.plugin.Initializable
import org.rs09.consts.Items
import org.rs09.consts.Scenery
import content.minigame.barbassault.bots.SmartBAPlayerScript
import content.minigame.barbassault.bots.SmartBABot
import core.game.bots.AIPlayer
import core.game.bots.GeneralBotCreator
@Initializable
class BADevHelper : CommandSet(Privilege.ADMIN) {
var babotcount = 0
override fun defineCommands() {
define ("maxbapoints") { player,_ ->
addBAPoints(player,BarbAssaultPoints(500, 500, 500,500))
return@define
}
define ("setbawave") { player,args ->
setBAWave(player,args[1].toInt())
return@define
}
define ("resetbapoints") { player,_ ->
setBAPoints(player,BarbAssaultPoints(0, 0, 0,0))
return@define
}
define ("resetbalevels") { player,_ ->
setBALevels(player,BarbAssaultLevels(0, 0, 0,0))
return@define
}
define("barbgame"){ player,_->
player.teleport(Location.create(1886, 5467, 0))
return@define
}
define("barbqueen"){ player,_->
player.teleport(Location.create(1885, 5403, 0))
return@define
}
//nods to the defender guide I found, as "suggested" defender gear.
define("defgear"){ player,_->
player.equipment.replace(Item(Items.ROPE_954),EquipmentContainer.SLOT_AMULET)
return@define
}
define("eggloot"){ player,_->
content.minigame.barbassault.arena.npcs.dropPenanceEggCluster(player)
return@define
}
define("cleanfloor"){ player,_->
val session = getBASession(player) ?: return@define
session.clearGroundItems()
return@define
}
define("spawnwave"){ player,_->
getBASession(player)?.forceWaveSpawn()
return@define
}
define("spawnwaveforce"){ player,_->
getBASession(player)?.forceWaveSpawn(true)
return@define
}
define("babot"){ player,_->
if (babotcount >= 25) {
sendMessage(player, "25 is max amount of BA bots allowed")
}else {
BABot(BALobby.MAIN_LOBBY.randomWalkableLoc)
babotcount++
}
return@define
}
define("smartbabot") { player, _ ->
if (babotcount >= 25) {
sendMessage(player, "25 is max amount of BA bots allowed")
return@define
}
SmartBABot(BALobby.MAIN_LOBBY.randomWalkableLoc)
babotcount++
sendMessage(player, "Spawned Smart BA bot.")
return@define
}
define("smartbaplayer") { player, _ ->
GeneralBotCreator(SmartBAPlayerScript(), player, true)
sendMessage(player, "Started Smart BA bot script on your player.")
return@define
}
define("babotstate") { player, args ->
val nameFilter = args.getOrNull(1)?.lowercase()
val bots = Repository.players
.filterIsInstance<AIPlayer>()
.filter { nameFilter == null || it.username.lowercase().contains(nameFilter) }
val bot = bots.minByOrNull { it.location.getDistance(player.location) }
if (bot == null) {
sendMessage(player, "No BA bot found.")
return@define
}
sendMessage(player, "${bot.username}: ${bot.customState} : loc: ${bot.location}")
return@define
}
define("iswait"){player,_ ->
val iswait = BALobby.isWaitingArea(player.location)
sendMessage(player,"isWaiting : ${iswait}")
}
define("ishall"){player,_ ->
val ishall = BALobby.isInHalls(player.location)
sendMessage(player,"isInhall : ${ishall}")
}
define("hch"){ player,args->
val chId = args[1].toIntOrNull()
if (chId != null) {
player.packetDispatch.sendInterfaceConfig(488, chId,true)
}
return@define
}
define("vch"){ player,args->
val chId = args[1].toIntOrNull()
if (chId != null) {
player.packetDispatch.sendInterfaceConfig(488, chId,false)
}
return@define
}
define("setifacemodel") { player, args ->
val modelId = args.getOrNull(1)?.toIntOrNull() ?: return@define
val iface = args.getOrNull(2)?.toIntOrNull() ?: return@define
val child = args.getOrNull(3)?.toIntOrNull() ?: return@define
val zoom = args.getOrNull(4)?.toIntOrNull() ?: 0
val pitch = args.getOrNull(5)?.toIntOrNull() ?: 0
val yaw = args.getOrNull(6)?.toIntOrNull() ?: 0
setInterfaceModel(player, modelId, iface, child, zoom, pitch, yaw)
}
//layer.packetDispatch.sendRepositionOnInterface(HealerIface.ID, 24, 45, 46)
define("setifaceoffset") { player, args ->
val iface = args.getOrNull(1)?.toIntOrNull() ?: return@define
val child = args.getOrNull(2)?.toIntOrNull() ?: return@define
val posX = args.getOrNull(3)?.toIntOrNull() ?: 0
val posY = args.getOrNull(4)?.toIntOrNull() ?: 0
player.packetDispatch.sendRepositionOnInterface(iface, child, posX, posY)
}
//sendItemZoomOnInterface(int itemId, int zoom, int interfaceId, int childId)
define("setifaceitem") { player, args ->
val iface = args.getOrNull(1)?.toIntOrNull() ?: return@define
val child = args.getOrNull(2)?.toIntOrNull() ?: return@define
val itemId = args.getOrNull(3)?.toIntOrNull() ?: 0
val zoom = args.getOrNull(4)?.toIntOrNull() ?: 0
sendItemZoomOnInterface(player,itemId, zoom, iface, child)
}
define("trap"){ player,args->
var thistrap = Location.create(1901, 5474, 0)
var thistrapscen = core.game.node.scenery.Scenery(Scenery.RUNNER_TRAP2_20135, Location(1901, 5474))
// RUNNER_TRAP2_20135
// RUNNER_TRAP1_20230
// BROKEN_TRAP0_20231
val scenery = getScenery(thistrap)
val emoteId = args[1].toIntOrNull()
animateScenery(scenery!!, emoteId!!)//5076
//replaceScenery(getScenery(thistrap)!!,Scenery.RUNNER_TRAP1_20230,40,thistrap)
return@define
}
//should not need during testing now.
define("rolekit", Privilege.ADMIN, "<role> <level>", "Gives Barbarian Assault kit") { player, args ->
data class RoleKit( val horns: IntArray? = null, val bags: IntArray? = null, val extraItems: List<Int> = emptyList() )
val roleKits = mapOf(
"healer" to RoleKit(
horns = intArrayOf(Items.HEALER_HORN_10526,Items.HEALER_HORN_10527,Items.HEALER_HORN_10528,Items.HEALER_HORN_10529,Items.HEALER_HORN_10530 ),
extraItems = listOf(10559,Items.HEALING_VIAL_10546, Items.POISONED_WORMS_10540, Items.POISONED_TOFU_10539,Items.POISONED_MEAT_10541)
),
"attacker" to RoleKit(
horns = intArrayOf(Items.ATTACKER_HORN_10516,Items.ATTACKER_HORN_10517,Items.ATTACKER_HORN_10518,Items.ATTACKER_HORN_10519,Items.ATTACKER_HORN_10520),
extraItems = listOf(10556)
),
"defender" to RoleKit(
horns = intArrayOf(Items.DEFENDER_HORN_10538),
extraItems = listOf(10558,Items.DEFENDER_HORN_10538,Items.WORMS_10515,Items.CRACKERS_10513,Items.TOFU_10514)
),
"collector" to RoleKit(
horns = intArrayOf(Items.COLLECTION_BAG_10521, Items.COLLECTION_BAG_10522, Items.COLLECTION_BAG_10523, Items.COLLECTION_BAG_10524, Items.COLLECTION_BAG_10525),
extraItems = listOf(10557,Items.COLLECTOR_HORN_10560)
)
)
if (args.size < 3) {
sendMessage(player,"Usage: ::rolekit <healer|attacker|collector|defender> <level 1-5>")
return@define
}
val roleName = args[1].lowercase()
val level = args[2].toIntOrNull()
if (level == null || level !in 1..5) {
sendMessage(player,"Invalid level. Use a number between 1 and 5.")
return@define
}
val role = roleKits[roleName]
if (role == null) {
sendMessage(player,"Unknown role: $roleName. Use healer, attacker, defender, or collector.")
return@define
}
player.inventory.add(Item(role.horns?.get(level - 1) ?:1 ))
role.extraItems.forEach { player.inventory.add(Item(it)) }
sendMessage(player,"Gave $roleName kit for level $level.")
return@define
}
}
}

View file

@ -0,0 +1,142 @@
package content.minigame.barbassault
import content.minigame.barbassault.BarbassBlackBoard.Companion.updateBlackBoard
import core.api.*
import core.game.component.Component
import core.game.global.action.ClimbActionHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.world.map.Location
import org.rs09.consts.Components
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import org.rs09.consts.Scenery
class BAListeners : InteractionListener {
override fun defineListeners() {
// addClimbDest(Location.create(2593, 5261, 0),Location.create(2593, 5261, 0))
on(Scenery.LADDER_20226, IntType.SCENERY, "Climb-down") { player, _ ->
// if (GameWorld.settings?.enable_barbassault != true) return@on false
//todo players are allowed to wear capes in to the lobby, But not the Wave Rooms
if (joinError(player)) return@on true
val loc = Location(2593 , 5261 )
ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, loc)
face(player,Location(2593, 5262))
return@on true
}
on(Scenery.LADDER_20227, IntType.SCENERY, "Climb-up") { player, _ ->
val loc = Location(2535 , 3572 )
player.inventory.removeAll(Items.SCROLL_10512)
ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, loc)
face(player,Location(2533, 3572))
return@on true
}
on(Scenery.BLACKBOARD_20134, IntType.SCENERY, "Read") { player, _ ->
//emote 5354
//todo Are we suppose to animate while reading?
updateBlackBoard(player)
return@on true
}
on(NPCs.COMMANDER_CONNAD_5029, IntType.NPC, "Get-rewards") { player, _ ->
player.interfaceManager.open(Component(Components.BARBASSAULT_REWARD_SHOP_491))
return@on true
}
on(NPCs.CAPTAIN_CAIN_5030, IntType.NPC, "Tutorial") { player, _ ->
player.interfaceManager.open(Component(Components.BARBASSAULT_TUTORIAL_496))
return@on true
}
}
private fun forbiddenItemsCheck(player: Player): String? {
val forbiddenRunes = setOf(
Items.AIR_RUNE_556,
Items.FIRE_RUNE_554,
Items.EARTH_RUNE_557,
Items.WATER_RUNE_555,
Items.BLOOD_RUNE_565,
Items.BODY_RUNE_559,
Items.CHAOS_RUNE_562,
Items.COSMIC_RUNE_564,
Items.ASTRAL_RUNE_9075,
Items.DEATH_RUNE_560,
Items.LAVA_RUNE_4699,
Items.MUD_RUNE_4698,
Items.SMOKE_RUNE_4697,
Items.STEAM_RUNE_4694,
Items.SOUL_RUNE_566,
Items.NATURE_RUNE_561
//todo extend not allowed items list.
//no Logs
//add food
//add ammo
//summoning pouches
)
for (item in player.inventory.toArray()) {
if (item != null) {
if (item.id in forbiddenRunes) {
//todo find source for this message
return "The barbarians do not allow outside food or runes."
}
}
}
return null
}
private fun familiarCheck(player: Player): String? {
if(player.familiarManager.hasFamiliar()) {
//todo find correct message
return "The barbarians do not allow followers."
}
return null
}
private fun joinError(player: Player): Boolean {
val errorMessage = forbiddenItemsCheck(player) ?: familiarCheck(player) ?: return false
player.sendMessage(errorMessage).also { return true }
}
}
/*
class BarbSelectRole : InterfaceListener {
//Team related
//Leader leaving room and causing disband "Your leader has left the room and therefore cleared the team!"
// //"Your recruiter exited the room."
//Accpting invite text " Your application has been accepted"
//The applicant declined your offer.
//the other player hasn't confirmed their role yet.
//the applicant has chosen a role[0]
//You removed the person from the team.
override fun defineInterfaceListeners() {
onOpen(493) { player, _ -> // change this for roles
val lines: Array<String> = player.getAttribute("ifaces:220:lines", arrayOf())
for(i in 0 until Math.min(lines.size, 15)) {
setInterfaceText(player, lines[i], 220, i+1)
//setInterfaceText(player, "${i}", 220, i+1)
}
return@onOpen true
}
}
}
*/

View file

@ -0,0 +1,380 @@
package content.minigame.barbassault
import core.api.*
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import core.plugin.Plugin
import core.tools.*
import org.rs09.consts.Components
import org.rs09.consts.Items
@Initializable
class BarbRewardInterface : ComponentPlugin() {
override fun open(player: Player?, component: Component?) {
super.open(player, component)
player ?: return
updateRewardButtons(player)
}
var selectedShopButton = -1
override fun handle(player: Player?, component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int): Boolean {
//https://youtu.be/AG5Iv-ysAW0?t=11
//todo find right values to highlight box blue when selecting.
if (button == 213) {
player!!.sendMessage("Accepted selection from button: $selectedShopButton")
acceptReward(player,selectedShopButton)
selectedShopButton = -1
return true
}
selectedShopButton = button
updateRewardButtons(player!!, button)
return true
}
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(Components.BARBASSAULT_REWARD_SHOP_491).plugin = this
return this
}
}
//may have client side script instead of this.
//dumps/scripts/2845.cs2 is for a newer revision with an overhauled interface.
fun updateRewardButtons(player: Player, selected: Int = -1) {
interfaceUpdate(player,selected)
//points @ bottom
val playerPoints = getBAPoints(player)
setInterfaceText(player, playerPoints.atk.toString(), 491, 219)
setInterfaceText(player, playerPoints.def.toString(), 491, 220)
setInterfaceText(player, playerPoints.col.toString(), 491, 221)
setInterfaceText(player, playerPoints.heal.toString(), 491, 222)
}
fun acceptReward(player:Player, selected :Int){
if (selected == -1 ) return
val reward = barbRewardShop.find { it.buttonChildId == selected } ?: return
if (!canBuyReward(player, reward, true)) return
when {
reward.item != null -> { addItem(player, reward.item, 1) }
// reward.gamble != null -> { }
reward.levelUp != null -> { increaseBARoleLevel(player,reward.levelUp) }
}
removeCost(player, reward)
updateRewardButtons(player)
}
fun rollGambleReward(tier: GambleTier): Item {
if (tier == GambleTier.HIGH) return Item(Items.DRAGON_CHAINBODY_13481,1)
if (tier == GambleTier.MEDIUM) return Item(Items.RUNE_ARROWP_PLUS_5621,52)
if (tier == GambleTier.LOW) return Item(Items.UNICORN_HORN_238,10)
return Item(Items.FLAX_1779,1)
}
fun interfaceUpdate(player: Player, selected: Int) {
val playerLevels = getBALevels(player)
val hasQueenKill = hasQueenKill(player)
barbRewardShop.forEach { reward ->
val isSelected = reward.buttonChildId == selected
val canBuy = canBuyReward(player, reward)
val LAVENDER = "<col=D6CCFD>"
var costText = reward.rewardCostText.text
val nameColor = if (isSelected) BLUE else LAVENDER
val resolvedText = if (reward.model != null) {
val text = reward.rewardText.resolve(playerLevels).text
val model = reward.model.resolve(playerLevels).modelId
val perkText = reward.perkText!!.resolve(playerLevels).text
costText = reward.rewardCostText.resolve(playerLevels).text
setInterfaceModel(player, model, 491, reward.model.childId, 2372)
setInterfaceText(player, perkText, 491, reward.perkText.childId)
text
} else {
reward.rewardText.text
}
setInterfaceText(player, nameColor + resolvedText, 491, reward.rewardText.childId)
val costColor = if (canBuy) GREEN else RED
setInterfaceText(player, costColor + costText, 491, reward.rewardCostText.childId)
if (reward.queenKillText != null) {
val color = if (hasQueenKill) GREEN else RED
setInterfaceText(player, color + reward.queenKillText.text, 491, reward.queenKillText.childId)
}
}
}
fun canBuyReward(player: Player, reward: RewardRow, buying: Boolean = false): Boolean {
val points = getBAPoints(player)
val hasQueenKill = hasQueenKill(player)
val barbLevels = getBALevels(player)
val resolvedCost = reward.cost.resolve(barbLevels)
fun requireQueenKill(base: Boolean): Boolean {
return if (buying && reward.queenKillText != null) base && hasQueenKill else base
}
return when (resolvedCost) {
is RewardCost.AllRoles -> requireQueenKill(
points.atk >= resolvedCost.amountPerRole &&
points.def >= resolvedCost.amountPerRole &&
points.col >= resolvedCost.amountPerRole &&
points.heal >= resolvedCost.amountPerRole
)
is RewardCost.AnyRole -> requireQueenKill( listOf( points.atk, points.def, points.col, points.heal).any { it >= resolvedCost.minimum })
is RewardCost.Coins -> requireQueenKill(inInventory(player, 995, resolvedCost.amount))
is RewardCost.Specific -> {
if (isBarbRewardMaxed(reward,barbLevels)) return false
requireQueenKill(
points.atk >= resolvedCost.required.atk &&
points.def >= resolvedCost.required.def &&
points.col >= resolvedCost.required.col &&
points.heal >= resolvedCost.required.heal
)
}
is RewardCost.Dynamic -> TODO()
}
}
fun isBarbRewardMaxed(reward: RewardRow, barbLevels: BarbAssaultLevels): Boolean {
val text = reward.rewardText.text.lowercase()
return when {
"attacker" in text -> barbLevels.atk >= 5
"defender" in text -> barbLevels.def >= 5
"collector" in text -> barbLevels.col >= 5
"healer" in text -> barbLevels.heal >= 5
else -> false
}
}
fun removeCost(player: Player, reward: RewardRow) {
val barbLevels = getBALevels(player)
val resolvedCost = reward.cost.resolve(barbLevels)
when (resolvedCost) {
is RewardCost.AllRoles -> {
if (reward.queenKillText != null) { setAttribute(player,"barbass:queen-kill", 0)}
removeBAPoints(player,BarbAssaultPoints(resolvedCost.amountPerRole, resolvedCost.amountPerRole, resolvedCost.amountPerRole, resolvedCost.amountPerRole))
}
is RewardCost.AnyRole -> { closeInterface(player);gambleDialogReward(player,resolvedCost.minimum) } //player.removeBarbPoints(BarbAssaultPoints(cost.minimum, cost.minimum, cost.minimum, cost.minimum))
is RewardCost.Coins -> {
if (reward.queenKillText != null) { setAttribute(player,"barbass:queen-kill", 0)}
removeItem(player, Item(Items.COINS_995, resolvedCost.amount), Container.INVENTORY)
}
is RewardCost.Specific -> removeBAPoints(player,resolvedCost.required)
else -> return
}
}
//https://youtu.be/KKUbBrJBab0?t=44
fun gambleDialogReward(player: Player, cost: Int) {
var costChoice: BarbRole? = null
//todo Finish Gamble reward
/*openDialogue(player, object : DialogueFile() {
override fun handle(componentID: Int, buttonID: Int) {
// this.npc = NPC( NPCs.COMMANDER_CONNAD_5029)
when (stage) {
0 -> {sendDialogueOptions(player,"Which points would you like to spend?", "Attacker Honor Points","Defender Honor Points","Collector Honor Points","Healer Honor Points")
addDialogueAction(player) { player , buttonId ->
when(buttonId) {
2 -> {costChoice = BarbRole.ATTACKER; stage++}
3 -> {costChoice = BarbRole.DEFENDER; stage++}
4 -> {costChoice = BarbRole.COLLECTOR; stage++}
5 -> {costChoice = BarbRole.HEALER; stage++}
}
//return@addDialogueAction
} }
2 -> sendNPCDialogue(player,NPCs.COMMANDER_CONNAD_5029,"Gamble you say! Let me take something from my big bag of prizes!").also { stage++}
3 -> npc("What can i find!").also { stage++ }
4 -> npc("Ah, found something!").also { stage++ }
5 -> npc("You've got...").also { stage++ }
6 -> sendItemDialogue(player,995,"ItemName!").also{end()}
}
}
}).also {
val barbPoints = player.getBarbPoints()
val costPointsSelected = when (costChoice) {
BarbRole.ATTACKER -> BarbAssaultPoints(atk = cost)
BarbRole.COLLECTOR -> BarbAssaultPoints(col = cost)
BarbRole.DEFENDER -> BarbAssaultPoints(def = cost)
BarbRole.HEALER -> BarbAssaultPoints(heal = cost)
else -> {return}
}
if (barbPoints.hasEnough(costPointsSelected)) {
player.removeBarbPoints(costPointsSelected)
} else {
player.sendMessage("You don't have enough ${costChoice!!.name.lowercase()} points to gamble.")
}
}*/
}
data class InterfaceModel( val childId: Int, val modelId: Int, val dynamicModelId: ((playerLevel: BarbAssaultLevels) -> Int)? = null) {
fun resolve(playerLevel: BarbAssaultLevels): InterfaceModel {
val resolvedID = dynamicModelId?.invoke(playerLevel) ?: modelId
return InterfaceModel(childId, resolvedID, null)
}
}
data class InterfaceText( val childId: Int, val text: String, val dynamicText: ((playerLevel: BarbAssaultLevels) -> String)? = null) {
fun resolve(playerLevel: BarbAssaultLevels): InterfaceText {
val resolvedText = dynamicText?.invoke(playerLevel) ?: text
return InterfaceText(childId, resolvedText, null)
}
}
sealed class RewardCost {
data class Specific(val required: BarbAssaultPoints) : RewardCost()
data class AllRoles(val amountPerRole: Int) : RewardCost()
data class AnyRole(val minimum: Int) : RewardCost()
data class Coins(val amount: Int) : RewardCost()
data class Dynamic(val generator: (BarbAssaultLevels) -> RewardCost) : RewardCost()
fun resolve(levels: BarbAssaultLevels): RewardCost = when (this) {
is Dynamic -> generator(levels)
else -> this
}
}
data class RewardRow(
val buttonChildId: Int,
val model : InterfaceModel? = null,
val rewardText: InterfaceText,
val perkText: InterfaceText? = null,
val rewardCostText: InterfaceText,
val queenKillText: InterfaceText? = null,
val cost: RewardCost,
val item: Int? = null,
val levelUp: BarbRole? = null,
val gamble: GambleTier? = null
)
enum class GambleTier {LOW, MEDIUM, HIGH }
//https://youtu.be/DZ9FqGFFVDw?t=4 -- 2009
//https://youtu.be/md3naJdy2MM?t=67 -- 2008
//https://youtu.be/u69JPBlDtVc?t=21 -- 2007
/*
Level | Honour Points | Collector | Healer | Attacker | Defender
------|---------------|------------------------------------------|-------------|-----------|-------------------
1 | Start level | Bag holds 2 eggs | Heals 10 hp | +1 Damage | Lure 4 spaces
2 | 200 | Bag holds 4 eggs + Egg conversion | Heals 15 hp | +2 Damage | Lure 5 spaces
3 | 300 | Bag holds 6 eggs + Conversion chance 40% | Heals 20 hp | +3 Damage | Lure 6 spaces
4 | 400 | Bag holds 7 eggs + Conversion chance 60% | Heals 25 hp | +4 Damage | Lure 8 spaces
5 | 500 | Bag holds 8 eggs + Conversion chance 80% | Heals 35 hp | +5 Damage | Lure 10 spaces
*/
val barbRewardShop = listOf(
RewardRow(81,
model = InterfaceModel(80,20522){ level -> (20521 + level.atk).coerceIn(20522,20525) },
rewardText = InterfaceText(82, "Attacker level up to 2"){ level -> if (level.atk >= 5) "Attacker level up complete" else "Attacker level up to ${level.atk + 1}"},
rewardCostText = InterfaceText(83, "200 Attacker points"){ level -> if (level.atk >= 5) " - Mastered - " else "${level.atk + 1}00 Attacker points"},
perkText = InterfaceText(85, "+2 bonus damage") { level -> if (level.atk >= 5) " - Mastered - " else "+${level.atk + 1} bonus damage" },
cost = RewardCost.Dynamic { level -> RewardCost.Specific(BarbAssaultPoints((level.atk + 1) * 100, 0, 0, 0))},
levelUp = BarbRole.ATTACKER
),
RewardRow(90,
model = InterfaceModel(89,20526){ level -> (20525 + level.col).coerceIn(20526,20529) },
rewardText = InterfaceText(91, "Collector level up to 2"){ level -> if (level.col >= 5) "Collector level up complete" else "Collector level up to ${level.col + 1}"},
rewardCostText = InterfaceText(92, "200 Collector points"){ level -> if (level.col >= 5) " - Mastered - " else "${level.col + 1}00 Collector points"},
perkText = InterfaceText(94, "Egg conversion") { level -> if (level.col <= 1) "Egg conversion" else if (level.col in 2..4) "Egg convert success ${(level.col - 2) * 20 + 40}%" else " - Mastered - " },
cost = RewardCost.Dynamic { level -> RewardCost.Specific(BarbAssaultPoints(0, (level.col + 1) * 100, 0, 0))},
levelUp = BarbRole.COLLECTOR
),
RewardRow(97,
model = InterfaceModel(96,20531){ level -> (20530 + level.def).coerceIn(20531,20534) },
rewardText = InterfaceText(98, "Defender level up to 2"){ level -> if (level.def >= 5) "Defender level up complete" else "Defender level up to ${level.def + 1}"},
rewardCostText = InterfaceText(99, "200 Defender points"){ level -> if (level.def >= 5) " - Mastered - " else "${level.def + 1}00 Defender points"},
perkText = InterfaceText(101, "Lure range 5") { level -> when (level.def) { 0, 1 -> "Lure range 5"; 2 -> "Lure range 6"; 3 -> "Lure range 8"; 4 -> "Lure range 10"; else -> " - Mastered - " } },
cost = RewardCost.Dynamic { level -> RewardCost.Specific(BarbAssaultPoints(0, 0, (level.def + 1) * 100, 0))},
levelUp = BarbRole.DEFENDER
),
RewardRow(104,
model = InterfaceModel(103,20538){ level -> (20537 + level.heal).coerceIn(20538,20541) },
rewardText = InterfaceText(105, "Healer level up to 2"){ level -> if (level.heal >= 5) "Healer level up complete" else "Healer level up to ${level.heal + 1}"},
rewardCostText = InterfaceText(106, "200 Defender points"){ level -> if (level.heal >= 5) " - Mastered - " else "${level.heal + 1}00 Healer points"},
perkText = InterfaceText(108, "Heal 15 points") { level -> when (level.heal) { 0, 1 -> "Heal 15 points"; 2 -> "Heal 20 points"; 3 -> "Heal 25 points"; 4 -> "Heal 35 points"; else -> " - Mastered - " } },
cost = RewardCost.Dynamic { level -> RewardCost.Specific(BarbAssaultPoints(0, 0, 0, (level.heal + 1) * 100))},
levelUp = BarbRole.HEALER
),
RewardRow(111,
rewardText = InterfaceText(112, "Penance Fighter hat"),
rewardCostText = InterfaceText(113, "275 points in each role"),
queenKillText = InterfaceText(229, "Kill Queen"),
cost = RewardCost.AllRoles(275),
item = Items.FIGHTER_HAT_10548
),
RewardRow(118,
rewardText = InterfaceText(119, "Penance Ranger hat"),
rewardCostText = InterfaceText(120, "275 points in each role"),
queenKillText = InterfaceText(228, "Kill Queen"),
cost = RewardCost.AllRoles(275),
item = Items.RANGER_HAT_10550
),
RewardRow(125,
rewardText = InterfaceText(126, "Penance Runner hat"),
rewardCostText = InterfaceText(127, "275 points in each role"),
queenKillText = InterfaceText(227, "Kill Queen"),
cost = RewardCost.AllRoles(275),
item = Items.RUNNER_HAT_10549
),
RewardRow(132,
rewardText = InterfaceText(133, "Penance Healer hat"),
rewardCostText = InterfaceText(134, "275 points in each role"),
queenKillText = InterfaceText(226, "Kill Queen"),
cost = RewardCost.AllRoles(275),
item = Items.HEALER_HAT_10547
),
RewardRow(139,
rewardText = InterfaceText(140, "Penance torso"),
rewardCostText = InterfaceText(141, "375 points in each role"),
queenKillText = InterfaceText(225, "Kill Queen"),
cost = RewardCost.AllRoles(375),
item = Items.FIGHTER_TORSO_10551
),
RewardRow(146,
rewardText = InterfaceText(147, "Penance skirt"),
rewardCostText = InterfaceText(148, "375 points in each role"),
queenKillText = InterfaceText(224, "Kill Queen"),
cost = RewardCost.AllRoles(375),
item = Items.PENANCE_SKIRT_10555
),
RewardRow(153,
rewardText = InterfaceText(154, "Penance boots"),
rewardCostText = InterfaceText(155, "100 points in each role"),
cost = RewardCost.AllRoles(100),
item = Items.RUNNER_BOOTS_10552
),
RewardRow(160,
rewardText = InterfaceText(161, "Penance gloves"),
rewardCostText = InterfaceText(162, "150 points in each role"),
cost = RewardCost.AllRoles(150),
item = Items.PENANCE_GLOVES_10553
),
RewardRow(167,
rewardText = InterfaceText(168, "Granite body"),
rewardCostText = InterfaceText(169, "95,000 coins"),
queenKillText = InterfaceText(230, "Kill Queen"),
cost = RewardCost.Coins(95000),
item = Items.GRANITE_BODY_10564
),
RewardRow(174,
rewardText = InterfaceText(175, "Gamble points - low items"),
rewardCostText = InterfaceText(176, "200 points (any role)"),
cost = RewardCost.AnyRole(200),
gamble = GambleTier.LOW
),
RewardRow(181,
rewardText = InterfaceText(182, "Gamble points - medium items"),
rewardCostText = InterfaceText(183, "400 points (any role)"),
cost = RewardCost.AnyRole(400),
gamble = GambleTier.MEDIUM
),
RewardRow(188,
rewardText = InterfaceText(189, "Gamble points - high items"),
rewardCostText = InterfaceText(190, "500 points (any role)"),
queenKillText = InterfaceText(223, "Kill Queen"),
cost = RewardCost.AnyRole(500),
gamble = GambleTier.HIGH
),
)

View file

@ -0,0 +1,82 @@
package content.minigame.barbassault
import core.api.MapArea
import core.api.getRegionBorders
import core.api.log
import core.api.produceGroundItem
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.item.GroundItemManager
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.tools.Log
import org.rs09.consts.Items
import org.rs09.consts.NPCs
class BATutorial: MapArea {
override fun defineAreaBorders(): Array<ZoneBorders> {
//block standard teleports.
BarbassTutNPCs.forEach {
npc -> npc.init()
npc.isWalks = false
}
spawnItems()
return arrayOf(getRegionBorders(7509))
}
override fun areaEnter(entity: Entity) {
//todo move tut maparea to barbtut
log(this::class.java, Log.FINE, "ENTERED BARBASS TUT")
}
override fun areaLeave(entity: Entity, logout: Boolean) {
//val player = entity as? Player ?: return
log(this::class.java, Log.FINE, "EXITED BARBASS TUT")
}
companion object {
data class TutorialItemSpawn(val itemId: Int, val location: Location)
val BarbassTutItems: List<TutorialItemSpawn> = listOf(
TutorialItemSpawn(Items.LOGS_11760, Location.create(1885, 5487, 0)),
TutorialItemSpawn(Items.LOGS_11760, Location.create(1886, 5486, 0)),
TutorialItemSpawn(Items.HAMMER_2347, Location.create(1888, 5482, 0))
)
val BarbassTutNPCs: List<NPC> = listOf(
NPC.create(NPCs.PENANCE_RANGER_5041, Location.create(1874, 5485, 0), Direction.SOUTH),
NPC.create(NPCs.PENANCE_FIGHTER_5040, Location.create(1880, 5486, 0), Direction.SOUTH),
NPC.create(NPCs.PENANCE_HEALER_5043, Location.create(1892, 5486, 0), Direction.SOUTH),
NPC.create(NPCs.PENANCE_RUNNER_5042, Location.create(1898, 5485, 0), Direction.SOUTH),
NPC.create(NPCs.MAJOR_DEFEND_5037, Location.create(1891, 5466, 0), Direction.SOUTH),
NPC.create(NPCs.MAJOR_HEAL_5038, Location.create(1882, 5466, 0), Direction.SOUTH),
NPC.create(NPCs.MAJOR_ATTACK_5035, Location.create(1882, 5477, 0), Direction.SOUTH),
NPC.create(NPCs.MAJOR_COLLECT_5036, Location.create(1891, 5477, 0), Direction.SOUTH),
NPC.create(NPCs.EGG_LAUNCHER_5026, Location.create(1896, 5474, 0), Direction.NORTH),
NPC.create(NPCs.EGG_LAUNCHER_5026, Location.create(1877, 5474, 0), Direction.NORTH)
)
fun spawnItems() {
val groundItems = GroundItemManager.getItems()
BarbassTutItems.forEach { (id, loc) ->
if (groundItems.none { it.id == id && it.location == loc && !it.isRemoved })
produceGroundItem(null, id, 1, loc).apply { forceVisible = true }
}
}
}
}
/*
https://www.youtube.com/watch?v=2A5hMOm0TMY
[CAPTAIN_CAIN_5030]: option [ Tutorial]
*/

View file

@ -0,0 +1,279 @@
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
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.system.timer.PersistTimer
import core.game.system.timer.RSTimer
import core.game.system.timer.TimerFlag
import core.tools.secondsToTicks
//thank you Ceikry
const val BA_SESSION_KEY = "ba-session"
/**
* @param player [Player]
*
* @return The current [BarbassaultSession] if the player is in a session,
* or `null` if they are not participating in Barbarian Assault.
*/
//getBASession(player)
fun getBASession(entity: Entity): BarbassaultSession? = entity.getAttribute(BA_SESSION_KEY)
fun setBASession(entity: Entity, session: BarbassaultSession?) {
if (session == null) {
entity.removeAttribute(BA_SESSION_KEY)
} else {
entity.setAttribute(BA_SESSION_KEY, session)
}
}
/**
* Retrieves the player's current Barbarian Assault points.
* @param player [Player]
* @return [BarbAssaultPoints] representing the player's stored points in each role:
* - `atk`: Attacker points
* - `col`: Collector points
* - `def`: Defender points
* - `heal`: Healer points
*
* Example usage:
* ```
* val points = getBAPoints(player)
* println("Attacker points: ${points.atk}")
* ```
*///getBAPoints(player)
fun getBAPoints(player: Player): BarbAssaultPoints = BarbAssaultPoints(
player.getAttribute("barbass:atk-points", 0),
player.getAttribute("barbass:col-points", 0),
player.getAttribute("barbass:def-points", 0),
player.getAttribute("barbass:heal-points", 0)
)
/**
* Sets the player's Barbarian Assault role points, clamping each to the allowed range (0 to 500)
* and saving them to persistent storage.
* @param player [Player]
* @param points [BarbAssaultPoints] containing the new point values for each role:
* - `atk`: Attacker points
* - `col`: Collector points
* - `def`: Defender points
* - `heal`: Healer points
*
* Example usage:
* ```
* val newPoints = BarbAssaultPoints(atk = 120, col = 80, def = 200, heal = 0)
* setBAPoints(player,newPoints)
* ```
*/
fun setBAPoints(player: Player, points: BarbAssaultPoints) {
val rolePoints = points.clamped()
player.setAttribute("/save:barbass:atk-points", rolePoints.atk)
player.setAttribute("/save:barbass:col-points", rolePoints.col)
player.setAttribute("/save:barbass:def-points", rolePoints.def)
player.setAttribute("/save:barbass:heal-points", rolePoints.heal)
}
/**
* @param player [Player]
* @param additional The points to be added to the player's current Barbarian Assault points.
* Values will be automatically clamped between 0 and 500.
*
* Example usage:
* ```
* // Give the player 30 attacker points and 10 collector points
* addBAPoints(player,BarbAssaultPoints(atk = 30, col = 10, def = 0, heal = 0))
* ```
*/
fun addBAPoints(player: Player,additional: BarbAssaultPoints) {
setBAPoints(player,getBAPoints(player) + additional)
//todo Add warning message if points hit cap.
//"You have maxed out your Healer Honor Points. You should spend them /n
//commands."
//https://youtu.be/rWRWL5PUj94?t=244
}
/**
*
* @param player [Player]
* @param toRemove The points to be subtracted from the player's current Barbarian Assault points.
* Values will be clamped so that no role goes below 0.
*
* Example usage:
* ```
* // Remove 15 attacker points and 5 defender points
* removeBAPoints(BarbAssaultPoints(atk = 15, col = 0, def = 5, heal = 0))
* ```
*/
fun removeBAPoints(player: Player, toRemove: BarbAssaultPoints) {
setBAPoints(player,getBAPoints(player) - toRemove)
}
/**
* @param player [Player]
*
* @return `true` if the player has one or more Queen kills,
* otherwise `false`.
*/
fun hasQueenKill(player: Player): Boolean = player.getAttribute("barbass:queen-kill", 0) > 0
/**
* Retrieves the player's Barbarian Assault role levels
* Defaults to level 1 for any role if not previously set.
* @param player [Player]
* @return [BarbAssaultLevels] containing the level for each role:
* - `atk`: Attacker level
* - `col`: Collector level
* - `def`: Defender level
* - `heal`: Healer level
*
* Example usage:
* ```
* val levels = getBALevels(player)
* println("Attacker Level: ${levels.atk}")
* ```
*/
fun getBALevels(player: Player): BarbAssaultLevels = BarbAssaultLevels(
player.getAttribute("barbass:atk-level", 1),
player.getAttribute("barbass:col-level", 1),
player.getAttribute("barbass:def-level", 1),
player.getAttribute("barbass:heal-level", 1)
)
/**
* Sets the player's Barbarian Assault role levels
* @param player [Player]
* @param levels The [BarbAssaultLevels] object containing levels for each role:
* - `atk`: Attacker level (15)
* - `col`: Collector level (15)
* - `def`: Defender level (15)
* - `heal`: Healer level (15)
*
* Example usage:
* ```
* val newLevels = BarbAssaultLevels(atk = 2, col = 1, def = 3, heal = 1)
* setBALevels(player,newLevels)
* ```
*/
fun setBALevels(player: Player,levels: BarbAssaultLevels) {
player.setAttribute("/save:barbass:atk-level", levels.atk)
player.setAttribute("/save:barbass:col-level", levels.col)
player.setAttribute("/save:barbass:def-level", levels.def)
player.setAttribute("/save:barbass:heal-level", levels.heal)
}
/**
* Increases the level of the specified Barbarian Assault role by 1, up to a maximum of 5.
*
* Only one role can be increased at a time. The result is clamped to ensure the level does not exceed 5.
* @param player [Player]
* @param role The [BarbRole] to increase the level of. Must be one of:
* - `BarbRole.ATTACKER`
* - `BarbRole.COLLECTOR`
* - `BarbRole.DEFENDER`
* - `BarbRole.HEALER`
*
* Example usage:
* ```
* increaseBARoleLevel(player,BarbRole.ATTACKER)
* ```
*/
fun increaseBARoleLevel(player: Player,role: BarbRole) {
val current = getBALevels(player)
val newLevels = when (role) {
BarbRole.ATTACKER -> current.copy(atk = current.atk + 1)
BarbRole.COLLECTOR -> current.copy(col = current.col + 1)
BarbRole.DEFENDER -> current.copy(def = current.def + 1)
BarbRole.HEALER -> current.copy(heal = current.heal + 1)
}.clamped()
setBALevels(player,newLevels)
}
/**
* Retrieves the player's current Barbarian Assault wave.
* @param player [Player]
*
* @return The current wave number. Defaults to `1` if no value is set.
*/
fun getBAWave(player: Player): Int? {
return player.getAttribute("barbass:wave", 1)
}
/**
* Sets the player's current Barbarian Assault wave.
* @param player [Player]
*
* @param wave The wave number to assign to the player.
*/
fun setBAWave(player: Player, wave: Int){
player.setAttribute("/save:barbass:wave", wave)
}
/**
* Gets the Barbarian Assault role assigned to the player.
*
* @param player The player whose role should be retrieved.
* @return The player's assigned [BarbRole], or `null` if the player is not in a Barbarian Assault session or team.
*/
fun getBARoleForPlayer(player: Player): BarbRole? {
val team = getBASession(player)?.team ?: return null
return team.getRoleForPlayer(player)
}
/**
* Checks whether the player currently has the specified Barbarian Assault role.
*
* @param player The player to check.
* @param role The role to compare against.
* @return `true` if the player has the specified role, otherwise `false`.
*/
fun isBARole(player: Player, role: BarbRole) = getBARoleForPlayer(player) == role
class BAPoison : PersistTimer (secondsToTicks(3), "BA-poison", flags = arrayOf(TimerFlag.ClearOnDeath)) {
lateinit var damageSource: Entity
var severity = 0
override fun run(entity: Entity): Boolean {
entity.impactHandler.manualHit(damageSource,(severity + 4) / 5,ImpactHandler.HitsplatType.POISON)
severity--
return severity > 0
}
override fun getTimer(vararg args: Any): RSTimer {
val timer = BAPoison()
timer.damageSource = args[0] as? Entity ?: return timer
timer.severity = args[1] as? Int ?: return timer
return timer
}
}
fun applyBAPoison (entity: Entity, source: Entity, severity: Int) {
val existingTimer = getTimer<BAPoison>(entity)
if (existingTimer != null) {
if (existingTimer.severity > severity) {
return
} else {
existingTimer.severity = severity
existingTimer.damageSource = source
}
} else {
registerTimer(entity, spawnTimer<BAPoison>(source, severity))
}
}
fun cureBAPoison(entity: Entity) {
if (!hasTimerActive<BAPoison>(entity)) {
return
}
removeTimer<BAPoison>(entity)
}
fun isBAPoisoned(entity: Entity): Boolean {
return hasTimerActive<BAPoison>(entity)
}

View file

@ -0,0 +1,77 @@
package content.minigame.barbassault
import content.minigame.barbassault.arena.ATTACKER_ICON
import content.minigame.barbassault.arena.COLLECTOR_ICON
import content.minigame.barbassault.arena.DEFENDER_ICON
import content.minigame.barbassault.arena.HEALER_ICON
import core.game.node.item.Item
import org.rs09.consts.Items
import org.rs09.consts.Scenery
val LureItems = intArrayOf(Items.WORMS_10515, Items.CRACKERS_10513, Items.TOFU_10514)
enum class BarbRole(val machineId: Int) {
ATTACKER(Scenery.ATTACKER_ITEM_MACHINE_20241),
DEFENDER(Scenery.DEFENDER_ITEM_MACHINE_20242),
HEALER(Scenery.HEALER_ITEM_MACHINE_20243),
COLLECTOR(Scenery.COLLECTOR_CONVERTER_21250)
}
data class BarbAssaultPoints( val atk: Int = 0, val col: Int = 0, val def: Int = 0, val heal: Int = 0) {
val total: Int get() = atk + col + def + heal
fun hasEnough(cost: BarbAssaultPoints): Boolean = atk >= cost.atk && col >= cost.col && def >= cost.def && heal >= cost.heal
operator fun minus(other: BarbAssaultPoints): BarbAssaultPoints = BarbAssaultPoints(atk - other.atk,col - other.col,def - other.def,heal - other.heal).clamped()
operator fun plus(other: BarbAssaultPoints): BarbAssaultPoints = BarbAssaultPoints(atk + other.atk,col + other.col,def + other.def,heal + other.heal).clamped()
fun clamped(): BarbAssaultPoints = BarbAssaultPoints(atk.coerceIn(0, 500), col.coerceIn(0, 500), def.coerceIn(0, 500), heal.coerceIn(0, 500) )
fun getPoints(role: BarbRole): Int = when (role) { BarbRole.ATTACKER -> atk; BarbRole.COLLECTOR -> col; BarbRole.DEFENDER -> def; BarbRole.HEALER -> heal; }
}
data class BarbAssaultLevels(val atk: Int, val col: Int, val def: Int, val heal: Int) {
fun clamped(): BarbAssaultLevels = BarbAssaultLevels( atk.coerceIn(1, 5), col.coerceIn(1, 5), def.coerceIn(1, 5), heal.coerceIn(1, 5) )
fun getLevel(role: BarbRole): Int = when (role) { BarbRole.ATTACKER -> atk; BarbRole.COLLECTOR -> col; BarbRole.DEFENDER -> def; BarbRole.HEALER -> heal; }
}
val ALL_BARBASS_ITEMS = arrayOf(
Item(ATTACKER_ICON),
Item(Items.ATTACKER_HORN_10516), Item(Items.ATTACKER_HORN_10517), Item(Items.ATTACKER_HORN_10518), Item(Items.ATTACKER_HORN_10519), Item(Items.ATTACKER_HORN_10520),
Item(DEFENDER_ICON),
Item(Items.WORMS_10515),Item(Items.CRACKERS_10513), Item(Items.TOFU_10514),
Item(Items.LOGS_11760),Item(Items.HAMMER_2347),
Item(Items.DEFENDER_HORN_10538),
Item(HEALER_ICON),
Item(Items.HEALING_VIAL_10546),Item(Items.HEALING_VIAL1_10545),Item(Items.HEALING_VIAL2_10544),Item(Items.HEALING_VIAL3_10543),Item(Items.HEALING_VIAL4_10542),
Item(Items.HEALER_HORN_10526),Item(Items.HEALER_HORN_10527),Item(Items.HEALER_HORN_10528),Item(Items.HEALER_HORN_10529),Item(Items.HEALER_HORN_10530),
Item(Items.POISONED_WORMS_10540),Item(Items.POISONED_TOFU_10539),Item(Items.POISONED_MEAT_10541),
Item(COLLECTOR_ICON),
Item(Items.COLLECTOR_HORN_10560),
Item(Items.COLLECTION_BAG_10521),Item(Items.COLLECTION_BAG_10522),Item(Items.COLLECTION_BAG_10523),Item(Items.COLLECTION_BAG_10524),Item(Items.COLLECTION_BAG_10525),
Item(Items.BLUE_EGG_10533),Item(Items.RED_EGG_10532),Item(Items.GREEN_EGG_10531),
)
object BAGraphics {
const val GREEN_LARGE_EXPLOSION_867 = 867
const val RED_LARGE_EXPLOSION_868 = 868
const val BLUE_LARGE_EXPLOSION_869 = 869
const val GREEN_EGG_BREAK_873 = 873
const val RED_EGG_BREAK_874 = 874
const val BLUE_EGG_BREAK_875 = 875
const val PENANCE_HEALER_BAD_BREATH_863 = 863
const val PENANCE_HEALER_GOOD_BREATH_864 = 864
const val GREEN_LARGE_EXPLOSION2_876 = 876
const val RED_EGG_EXPLOSION_975 = 975
const val GREEN_EGG_PROJECTILE_977 = 977
const val RED_EGG_PROJECTILE_978 = 978
const val Blue_EGG_PROJECTILE_978 = 979
const val YELLOW_EGG_PROJECTILE_980 = 980
const val STUNN_BIRDYS_981 = 981
}

View file

@ -1,18 +0,0 @@
package content.minigame.barbassault
import core.api.MapArea
import core.api.getRegionBorders
import core.game.node.entity.Entity
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneType
class BarbAssaultArea : MapArea {
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(getRegionBorders(7509))
}
override fun areaEnter(entity: Entity) {
zone.zoneType = ZoneType.BARBARIAN_ASSAULT.id
super.areaEnter(entity)
}
}

View file

@ -27,6 +27,7 @@ class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) {
override fun open(vararg args: Any?): Boolean {
npcl(FacialExpression.FRIENDLY, "Hello, there, adventurer. Say, you wouldn't happen to be interested in purchasing a Fighter Torso would you?")
stage = 0
player.setAttribute("barbass:tut", true)
return true
}
@ -72,4 +73,26 @@ class CaptainCainDialogue(player: Player? = null) : DialoguePlugin(player) {
return intArrayOf(NPCs.CAPTAIN_CAIN_5030)
}
}
}
/**
* lets keep the meme of WHEN you were able to buy a torso and ryans promise <3
*
* dialogoption to -> I heard you could sell me a fighter's torso.
* FacialExpression.HALF_GUILTY, NPC: Sell you a torso? Oh, I couldnt possibly ,
* Im only here to teach adventurers the basics.
*
* FacialExpression.ASKING Player: But people say you did back when Ryan promised Barbarian Assault.
*
* NPC: People say a lot of things.
* Ive never officially offered anything like that.
* Official operations only started on {RELEASE_DATE} anything before that is unofficial.
*
* Player:The promise was made on $start. Some adventurers even counted its been $days days since then.
*
* FacialExpression.SUSPICIOUS NPC: I see the adventurers remember everything......
*
* FacialExpression.ANNOYED NPC: Fine, keep your rumours. But dont expect me to give a torso for them.
* Now, back to training!!
*
*/

View file

@ -0,0 +1,110 @@
package content.minigame.barbassault
import core.api.addItem
import core.api.freeSlots
import core.api.openInterface
import core.game.dialogue.DialoguePlugin
import core.game.dialogue.FacialExpression
import core.game.dialogue.Topic
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import core.tools.END_DIALOGUE
import org.rs09.consts.Components
import org.rs09.consts.Items
import org.rs09.consts.NPCs
@Initializable
class CommanderConnad(player: Player? = null) : DialoguePlugin(player) {
override fun newInstance(player: Player?): DialoguePlugin {
return CommanderConnad(player)
}
override fun open(vararg args: Any?): Boolean {
val tutComplete = player.getAttribute("barbass:tut", false)
stage = if (tutComplete) 5 else 0
handle(0,0)
return true
}
override fun handle(componentID: Int, buttonID: Int): Boolean {
val barbasspoints = getBAPoints(player)
val displayName = player.name.replaceFirstChar { it.uppercaseChar() }
when (stage) {
0 -> playerl(FacialExpression.FRIENDLY, "Hello?").also { stage++ }
1 -> npcl(FacialExpression.FRIENDLY, "Sorry soldier, you don't have permission to speak with me yet. Go and talk to the Captain.").also { stage = END_DIALOGUE }
5 -> playerl(FacialExpression.FRIENDLY, " I believe you might be able to reward me for my progress in this arena?.").also { stage++ }
6 -> npcl(FacialExpression.FRIENDLY,"Well that depends upon your skill levels for each role.").also { stage++ }
7 -> npcl(FacialExpression.ASKING,"What's your name soldier?").also { stage++ }
8 -> playerl(FacialExpression.FRIENDLY, "${displayName}, Sir!").also { stage++ }
9 -> npcl(FacialExpression.FRIENDLY," Okay. What exactly do you want to know, ${displayName}?").also { stage++ }
10 -> showTopics(
Topic(FacialExpression.ASKING,"Can I trade some of my points?",15),
Topic(FacialExpression.ASKING,"What points do I have so far, sir?",25),
Topic(FacialExpression.ASKING,"Could I have a 'Queen Help' book?", queenBookStage(player)),
Topic(FacialExpression.ASKING,"Could I have my current wave reset to one?", areWaveOne(player)),
Topic(FacialExpression.FRIENDLY,"That is all, sir.",100)
)
//Open RewardInterface
15-> npcl(FacialExpression.FRIENDLY,"Of course.").also { stage++ }
16 -> {
end()
openInterface(player,Components.BARBASSAULT_REWARD_SHOP_491)
}
//Point Check
25 -> npcl(FacialExpression.FRIENDLY,"Let me check...").also { stage = getBarbPointStage(barbasspoints.total) }
26 -> npcl(FacialExpression.FRIENDLY,"You have absolutely no points, whatsoever. Best you get back into that arena!").also { stage = 10 }
27 -> npcl(FacialExpression.FRIENDLY,"You have: ${barbasspoints.atk} Attacker Honour Points, ${barbasspoints.def} Defender Honour Points, ${barbasspoints.col} Collector Honour Points and ${barbasspoints.heal} Healer Honour Points.").also { stage = 10 }
//Get Queen book.
50 -> npcl(FacialExpression.FRIENDLY,"By all means.").also {
addItem(player, Items.QUEEN_HELP_BOOK_10562, 1)
stage = 10
}
//Already have Queen book
51 -> npcl(FacialExpression.ANNOYED,"You already have one!").also { stage++ }
52 -> playerl(FacialExpression.WORRIED,"Oh yes, sorry.").also { stage = 10}
//not enough Inventory Space
53 -> playerl(FacialExpression.WORRIED,"Oh, no. Wait, I don't have enough space to carry one.").also { stage++ }
54 -> npcl(FacialExpression.FRIENDLY,"Well, you can come back and get one at any time from me.").also { stage = 10 }
//Already have QueenBook
75 -> npcl(FacialExpression.ANNOYED," You do realise that you're already on wave one?").also { stage++ }
76 -> playerl(FacialExpression.WORRIED,"Ah. Yes. Sorry about that.").also { stage = END_DIALOGUE}
//todo find Correct Dialog for wave reset
77 -> npcl(FacialExpression.ASKING,"I can change it down to wave 1, Are you sure you want that?").also { stage++ }
78 -> showTopics(
Topic(FacialExpression.FRIENDLY, "Yes, reset my wave to one.",79),
Topic(FacialExpression.FRIENDLY, "No, I want to stay on wave ${getBAWave(player)}.",10)
)
79 -> {
npcl(FacialExpression.ASKING,"Very well.").also { stage = END_DIALOGUE }
player.setAttribute("/save:barbass:wave", 1)
}
//END
100 -> npcl(FacialExpression.FRIENDLY,"Very well, on your way.").also { stage = END_DIALOGUE }
}
return true
}
private fun getBarbPointStage(total: Int): Int {
if (total == 0 ) return 26
return 27
}
private fun queenBookStage(player: Player?): Int {
if (freeSlots(player!!) == 0) return 53
if (player.hasItem(Item(Items.QUEEN_HELP_BOOK_10562))) return 51
return 50
}
private fun areWaveOne(player: Player): Int {
if (getBAWave(player) != 1) return 77
return 75
}
override fun getIds(): IntArray {
return intArrayOf(NPCs.COMMANDER_CONNAD_5029)
}
}
//[COMMANDER_CONNAD_5029] Dialog: https://www.youtube.com/watch?v=wb1InggbaZc

View file

@ -0,0 +1,55 @@
package content.minigame.barbassault
import core.api.openDialogue
import core.api.sendDialogue
import core.game.dialogue.DialogueFile
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import org.rs09.consts.Scenery
class PenanceStatueDialogues : InteractionListener {
val statueToDialogId = mapOf(
Scenery.PENANCE_FIGHTER_STATUE_20164 to 1,
Scenery.PENANCE_HEALER_STATUE_20165 to 5,
Scenery.PENANCE_RANGER_STATUE_20167 to 10,
Scenery.PENANCE_RUNNER_STATUE_20166 to 15,
Scenery.PENANCE_QUEEN_SPAWN_STATUE_20168 to 20
)
val STATUES: IntArray = statueToDialogId.keys.toIntArray()
override fun defineListeners() {
on(STATUES, IntType.SCENERY, "Inspect") { player, node ->
openDialogue(player, object : DialogueFile() {
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
0 -> sendDialogue(player, "There seems to be a plaque on the statue. It says:").also { stage = statueToDialogId[node.id] ?: -1 }
//FIGHTER STATUE
1 -> sendDialogue(player, "'The Penance Fighter - With the single goal to attack all that may threaten its Queen, this monster uses it's giant claws to shred its prey, and can only be stopped with brute force. Sharing the telekinetic trait of the other Penance monsters, the Penance Fighter can anticipate and adapt to").also { stage++ }
2 -> sendDialogue(player, "defend itself. Bearing this in mind, it's important when fighting this monster to change attack style periodically to ensure damage is dealt. Most combatants will be glad to notice this creature (like the other Penance) has only one eye, and hence its depth perception is not all that it could be! Otherwise, those claws would soon destroy the most").also { stage++ }
3 -> sendDialogue(player, "experienced of warriors!'").also { stage = 26 }
//HEALER STATUE
5 -> sendDialogue(player,"'The Penance Healer - This monster could have arguably been called a 'Penance Poisoner', for it has a dual role - both healing other Penance and poisoning any creature it sees as a threat. It performs these roles with equal importance and switches from one to the other as soon as it has the").also { stage++ }
6 -> sendDialogue(player,"chance of finding a target. The best way to stop a Penance Healer is fighting it at its own game. The Penance Healer is partial to food, which it uses to create its own chemicals for curing and poisoning. As a result it has to be very careful what it eats, and has a mechanism by which it will be immune to certain types of poisonous foods at").also { stage++ }
7 -> sendDialogue(player,"different times. With this information in mind, it is in fact possible to poison the Penance Healer by using the right type of food at the right time.'").also { stage = 26 }
//RANGER STATUE
10 -> sendDialogue(player,"'The Penance Ranger - These creatures store a highly corrosive liquid in the abdomen at the base of their bodies. It is believed that this was initially used for breaking up food matter to be passed on to the Queen for digestion. With the increase of easily digestible food sources (e.g. 'daring'").also { stage++ }
11 -> sendDialogue(player,"adventurers), this functionality has been removed. It now seems to be used for offensive means in the form of projectiles. The only way to defeat this monster is to confront it with brute force, as it has no convenient weakness. That being said, the Penance Ranger can anticipate and adapt to attacks using the telekenisis common between").also { stage++ }
12 -> sendDialogue(player,"all Penance, and hence, when taking on this monster, it is important to change attack style periodically to perform damage.'").also { stage = 26 }
//RUNNER STATUE
15 -> sendDialogue(player,"'The Penance Runner - These creatures are attracted to the scent of their Queen - charging to her aid in the hope of creating a barrier to protect her from threat. Their slender legs aid in motion, and their bulky bodies are effective shields and a strong barrier when linked together. And so,").also { stage++ }
16 -> sendDialogue(player,"they are willing to waste their own lives in the simple interest of protecting their Queen. Using this interesting trait against them, Penance Runners are lured in the arena by the exit cave, which is doused with this scent. This is used to test combatants in how many Penance Runners manage to get past and how many are stopped").also { stage++ }
17 -> sendDialogue(player,"with the use of the traps. Penance Runners are an energetic group, and therefore take any opportunity to eat and restore energy. With this in mind, laying down food within smelling distance of the Runners will lure them along. However, they are not stupid, and look for different food to ensure they are not so easily tricked... Hence,").also { stage++ }
18 -> sendDialogue(player,"giving them the wrong food will send them running back to their caves. It's an interesting evolutionary trait, which is transmitted telepathically between Penance Runners... One which we carefully intercept.'").also { stage = 26 }
//QUEENSPAWN STATUE
20 -> sendDialogue(player,"'The Penance Spawn - You're only likely to see these creatures in close proximity to their creator - the dreaded Penance Queen. All Penance begin their lives in this form, mutating into the seperate classes of Penance depending on the lack or abundance of other classes in the local environment.").also { stage++ }
21 -> sendDialogue(player,"Through telepathic communication, the Queen is able to determine the number of each class of Penance (Fighter, Ranger, Healer or Runner) and communicate to her spawn the form they should take on to fill any gaps in numbers. If the spawn receives no communication from a Queen, it turns into a Queen itself, because").also { stage++ }
22 -> sendDialogue(player,"of the evident neccessity of such a role. These spawn will attack on sight, but a few hits by any means should stop these slimy creatures.'").also { stage = 26 }
//END OF DIALOGUE
26 -> end()
}
}
})
return@on true
}
}
}

View file

@ -0,0 +1,64 @@
package content.minigame.barbassault
import content.global.handlers.iface.BookInterface
import content.global.handlers.iface.BookLine
import content.global.handlers.iface.Page
import content.global.handlers.iface.PageSet
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import org.rs09.consts.Items
class QueenHelpBook : InteractionListener {
companion object {
private val TITLE = "Queen Help"
private val CONTENTS = arrayOf(
PageSet(
Page( //1
BookLine("<col=ff0000>1) <col=0b5394>Collector: <col=000000>Pick up ", 55),
BookLine("yellow egg. pass to", 56),
BookLine("healer.", 57),
BookLine("Take omega egg from", 58),
BookLine("Defender. Load in to ", 59),
BookLine("turret.", 60),
BookLine("<col=ff0000>2) <col=0b5394>Healer:<col=000000> Take yellow", 62),
BookLine("egg from Collector.", 63),
BookLine("Poison egg in pool. Pass", 64),
BookLine("to Attacker.", 65),
),
Page( //2
BookLine("", 66),
BookLine("<col=ff0000>3)<col=0b5394> Attacker:<col=000000> Take", 67),
BookLine("poisoned, yellow egg from", 68),
BookLine("healer Add spikes from", 69),
BookLine("mushroom. Pass to", 70),
BookLine("Defender.", 70),
BookLine("", 72),
BookLine("<col=ff0000>4)<col=0b5394> Defender:<col=000000> Take ", 73),
BookLine("poisoned, spiked, yellow", 74),
BookLine("from Attacker. Dunk", 75),
BookLine("in lava. Pass to Collector.", 76),
BookLine("", 77),
BookLine("", 78)
)
)
)
private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean {
BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS)
return true
}
}
override fun defineListeners() {
on(Items.QUEEN_HELP_BOOK_10562, IntType.ITEM, "read") { player, _ ->
BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display)
return@on true
}
}
}
//[QUEEN_HELP_BOOK_10562] // https://www.youtube.com/watch?v=r1x8xbz3JAA

View file

@ -0,0 +1,97 @@
package content.minigame.barbassault
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.BarbassaultState
import core.api.log
import core.game.activity.ActivityManager
import core.game.activity.ActivityPlugin
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.map.Location
import core.game.world.map.zone.ZoneRestriction
import core.plugin.Initializable
import core.tools.Log
private val sessions = mutableListOf<BarbassaultSession>()
private var activity: BAActivity? = null
@Initializable
open class BAActivity : ActivityPlugin("barbassault",false, true, true,ZoneRestriction.CANNON,ZoneRestriction.FIRES,ZoneRestriction.FOLLOWERS,ZoneRestriction.RANDOM_EVENTS) {
init {
activity = this
}
override fun configure() {
GameWorld.Pulser.submit(object : Pulse(10) {
override fun pulse(): Boolean {
sessions.removeIf { session ->
val shouldRemove = session.state == BarbassaultState.END
if (shouldRemove) {
log(this.javaClass, Log.FINE, "Removing session: $session")
}
shouldRemove
}
return false
}
})
}
override fun start(player: Player?, login: Boolean, vararg args: Any?): Boolean {
super.start(player, login, *args)
return true
}
override fun enter(e: Entity?): Boolean { return super.enter(e) }
override fun continueAttack(e: Entity?, target: Node?, style: CombatStyle?, message: Boolean): Boolean {
//BERR must of been good, idk why I put this in here
return super.continueAttack(e, target, style, message)
}
override fun death(e: Entity, killer: Entity?): Boolean {
log(this.javaClass, Log.FINE,"${e.name} has died in Barbass")
val session = getBASession(e) ?:return false
if (e is Player) {
session.state = BarbassaultState.DEATH
e.getProperties().setTeleportLocation(null)
return true
}
if (e is NPC) {
// dropPenanceEggCluster(e)
session.sessionNpcs.remove(e)
session.pennanceClearedMessage(e)
}
return false
}
override fun newInstance(p: Player?): ActivityPlugin {
ActivityManager.register(this)
return this
}
override fun getSpawnLocation(): Location {
return Location.create(2593, 5264, 0)
}
//override when !2265 passes (void recovery)
fun recover(player: Player){
//removeBarbass items
//super.recover(player)
}
companion object {
fun createSession(leader: Player): BarbassaultSession {
val activity = ActivityManager.getActivity("barbassault") as BAActivity
val session = BarbassaultSession(activity)
sessions += session
setBASession(leader,session)
return session
}
}
}

View file

@ -0,0 +1,508 @@
package content.minigame.barbassault.arena
import content.minigame.barbassault.*
import content.minigame.barbassault.BAGraphics.BLUE_EGG_BREAK_875
import content.minigame.barbassault.BAGraphics.GREEN_EGG_BREAK_873
import content.minigame.barbassault.BAGraphics.RED_EGG_BREAK_874
import content.minigame.barbassault.arena.BarbAssEvent.EggCollected
import content.minigame.barbassault.arena.BarbAssEvent.HitpointsHealed
import content.minigame.barbassault.arena.BarbAssEvent.WrongPoisonUsed
import content.minigame.barbassault.arena.BarbassaultSession.AttackerStyle
import content.minigame.barbassault.arena.npcs.PENANCE_HEALER_IDS
import content.minigame.barbassault.arena.scenery.EggCannon
import content.minigame.barbassault.arena.scenery.EggCannon.Companion.loadEggHopper
import content.minigame.barbassault.arena.scenery.EggCannon.Companion.loadHopperEggs
import content.minigame.barbassault.arena.scenery.EggCannon.Companion.showEggHopperCount
import core.api.*
import core.game.global.action.ClimbActionHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.combat.ImpactHandler
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_ACCURATE
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_AGGRESSIVE
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_CONTROLLED
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_DEFENSIVE
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.entity.player.info.LogType
import core.game.node.entity.player.info.PlayerMonitor
import core.game.node.item.GroundItem
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.GameWorld.Pulser
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import getCollectorBag
import org.rs09.consts.Items
import org.rs09.consts.Scenery
import resetCollectorBag
//1889 5407 - Barb assualt QUEEN area -- 7508
//1891 5465 - barb assualt AREA under
val PENANCE_QUEEN_SPAWN_LOC = Location.create(1884, 5405, 0)
//sendMessage(player,"The barbarians forbid the use of prayer in their arena as they don't ~ value such methods!")
//should be multicombat
val ATTACKER_ICON = 10556
val DEFENDER_ICON = 10558
val COLLECTOR_ICON = 10557
val HEALER_ICON = 10559
class BarbassaultArenaListener : InteractionListener {
val ROLE_ICON = intArrayOf( ATTACKER_ICON, DEFENDER_ICON, COLLECTOR_ICON, HEALER_ICON )
val Fix_trap_IDs = intArrayOf( Scenery.RUNNER_TRAP1_20230, Scenery.BROKEN_TRAP0_20231 )
val All_HEALING_VIALS = intArrayOf(Items.HEALING_VIAL_10546,Items.HEALING_VIAL1_10545,Items.HEALING_VIAL2_10544,Items.HEALING_VIAL3_10543,Items.HEALING_VIAL4_10542 )
val ALL_COLLECTION_BAGS = intArrayOf(Items.COLLECTION_BAG_10521,Items.COLLECTION_BAG_10522,Items.COLLECTION_BAG_10523,Items.COLLECTION_BAG_10524,Items.COLLECTION_BAG_10525)
object BA_ARENA { val MAIN = 7509; val QUEEN = 7508; val all = listOf(MAIN, QUEEN) }
override fun defineListeners() {
//Wrong food message "That's the wrong type of poisoned food to use! Penalty!"
on(Scenery.EGG_LAUNCHER_20133, IntType.SCENERY, "Shoot") { player, node ->
EggCannon.openTurretIface(player)
return@on true
}
on(Scenery.EGG_HOPPER_20264, IntType.SCENERY, "Load","Look-in") { player, node ->
val loc = player.location
val optionClicked = player.getAttribute<String>("interact:option")?.lowercase() ?: return@on false
when(optionClicked){
"load" -> loadEggHopper(player)
"look-in" -> showEggHopperCount(player)
}
player.faceLocation(location(loc.x,loc.y+1,0))
return@on true
}
onUseWith(IntType.SCENERY, EggCannon.ALL_EGGS,Scenery.EGG_HOPPER_20264){ player, used, with ->
val loc = player.location
player.faceLocation(location(loc.x,loc.y+1,0))
loadHopperEggs(player,used.id )
return@onUseWith true
}
//wrapperID for hopper
on(20267, IntType.SCENERY, "Load","Look-in") { player, node ->
sendMessage(player,"hopper")
return@on true
}
setDest(IntType.SCENERY, intArrayOf(Scenery.EGG_HOPPER_20264), "use","Load","Look-in") { player, node ->
//work around for running off to org location instead of copy location.
//Player Name's Parlor chairs Should fix this!
return@setDest sessionDestination(player as Player,node,Direction.SOUTH)
}
on(Fix_trap_IDs, IntType.SCENERY, "Fix") { player, node ->
if (hasTools(player)) { return@on true }
playerChangeScenery(player,5416,node,Scenery.RUNNER_TRAP2_20135,5,"You fixed the Penance Runner trap.")
return@on true
}
on(Scenery.PENANCE_CAVE_20237, IntType.SCENERY, "block") { player, node ->
//restrict to role
if (hasTools(player)) { return@on true }
playerChangeScenery(player,5417,node,Scenery.PENANCE_CAVE_20238,4)
getBASession(player)?.setCaveBlocked(node,true)
return@on true
}
on(Scenery.PENANCE_CAVE_20238, IntType.SCENERY, "demolish") { player, node ->
playerChangeScenery(player,5418,node,Scenery.PENANCE_CAVE_20239,5)
getBASession(player)?.setCaveBlocked(node,false)
return@on true
}
on(Scenery.PENANCE_CAVE_20239, IntType.SCENERY, "fix") { player, node ->
if (hasTools(player)) { return@on true }
playerChangeScenery(player,5417,node,Scenery.PENANCE_CAVE_20238,4)
return@on true
}
on(Scenery.LADDER_20194, IntType.SCENERY, "Climb-up") { player, _ ->
sendDialogueOptions(player, "Are you sure you want to leave?", "Yes", "No")
addDialogueAction(player) { _, buttonId ->
if (buttonId == 2) {
val animationDuration = animationDuration(ClimbActionHandler.CLIMB_UP)
player.lock(animationDuration)
player.animate(ClimbActionHandler.CLIMB_UP)
Pulser.submit(object : Pulse(animationDuration) { override fun pulse(): Boolean {getBASession(player)?.forfeit(); return true }})
}
}
return@on true
}
//you healed $heal.count hitpoints.
//You've been healed $amount hitpoints and your poison cured.
on(Scenery.HEALER_SPRING_20150, IntType.SCENERY, "Drink-from") { player, _ ->
if (isBARole(player, BarbRole.HEALER)) {
player.animate(Animation(5407))
player.lock(5)
//todo use quescript here instead
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()
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
}
})
}
return@on true
}
onUseWith(IntType.NPC, intArrayOf(Items.POISONED_TOFU_10539,Items.POISONED_MEAT_10541,Items.POISONED_WORMS_10540), *PENANCE_HEALER_IDS) { player, used, target ->
val session = getBASession(player) ?: return@onUseWith true
val currentCall = (session.roleCalls[BarbRole.HEALER] as? BarbassaultSession.HealerCall)?.required?.itemId
removeItem(player,used)
if (used.id == currentCall) {
applyBAPoison(target as Entity,player,20)
target.impactHandler.manualHit ( player,4, ImpactHandler.HitsplatType.POISON )
return@onUseWith true
}
sendMessage(player,"that's the wrong type of poisoned food to use! Penalty!")
session.eventBus.emit(WrongPoisonUsed(player))
return@onUseWith true
}
onUseWithPlayer(*All_HEALING_VIALS) { player, _, target ->
target as Player
//todo find Healing sound
val targetPoisoned = isBAPoisoned(target)
if (target.skills.lifepoints >= target.skills.maximumLifepoints && !targetPoisoned) {
sendMessage(player, "The player's hitpoints are full.")
return@onUseWithPlayer true
}
val current = All_HEALING_VIALS.firstOrNull { player.inventory.containsAtLeastOneItem(it) }?: return@onUseWithPlayer true
val index = All_HEALING_VIALS.indexOf(current)
if (index > 0) {
queueScript(player, 0) {
val session = getBASession(player)
val next = All_HEALING_VIALS[index - 1]
player.inventory.remove(Item(current))
player.inventory.add(Item(next))
val restoreAmount =
minOf(getHealerHealAmount(player), target.skills.maximumLifepoints - target.skills.lifepoints)
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))
return@queueScript stopExecuting(player)
}
}
return@onUseWithPlayer true
}
on(Scenery.HEALER_SPRING_20150, IntType.SCENERY, "Take-from") { player, _ -> fillHealerVial(player); return@on true }
onUseAnyWith( IntType.SCENERY,Scenery.HEALER_SPRING_20150,*All_HEALING_VIALS) { player, used, with -> fillHealerVial(player); return@onUseAnyWith true}
onUnequip(ROLE_ICON) { player, _ ->
if (getBASession(player)?.state == BarbassaultState.IN_ARENA){
sendMessage(player, "You can't remove that!") // is there a proper message for this?
return@onUnequip false
}
return@onUnequip true
}
onEquip(ROLE_ICON) { player, _ ->
if (getBASession(player)?.state == BarbassaultState.IN_ARENA) {
sendMessage(player, "You can't remove that!")
return@onEquip false
}
return@onEquip true
}
on(LureItems, IntType.GROUNDITEM, "Take") { player, node ->
val session = getBASession(player) ?: return@on true
if (session.team.getRoleForPlayer(player) != BarbRole.DEFENDER) {
sendMessage(player,"not a Defender")
}
//if (getCollectorBag(player).addToBag())
session.pickUpGroundItem(player, node as BAGroundSpawn)
return@on true
}
on(LureItems, IntType.ITEM, "Drop") { player, node ->
val session = getBASession(player) ?: return@on true
val currentCall = (session.roleCalls[BarbRole.DEFENDER] as? BarbassaultSession.DefenderCall)?.required
//sendMessage(player,currentCall?.itemId.toString())
val flag = node.id == currentCall?.itemId
handleDropItem(player,node,flag)
return@on true
}
on(EggCannon.ALL_EGGS, IntType.GROUNDITEM, "Take") { player, node ->
if (node !is GroundItem) return@on true
val session = getBASession(player) ?: return@on true
//todo find authentic message
if (session.team.getRoleForPlayer(player) != BarbRole.COLLECTOR) { sendMessage(player, "You need to be a collector to do that");return@on false }
val currentCall = (session.roleCalls[BarbRole.COLLECTOR]as? BarbassaultSession.CollectorCall)?.required
if (node.id != currentCall?.itemId) { handleWrongEgg(player,node); return@on false }
val eggInBag = getCollectorBag(player).addToBag(node)
if (eggInBag) {
session.destroyGroundItem(node as BAGroundSpawn)
session.eventBus.emit(EggCollected(player,1))
return@on true
}
val didPickUp = session.pickUpGroundItem(player, node as BAGroundSpawn)
if (!didPickUp) { handleWrongEgg(player,node,false); return@on true }
session.eventBus.emit(EggCollected(player,1))
return@on true
}
on(ALL_COLLECTION_BAGS, IntType.ITEM, "Look-In") { player, node ->
getCollectorBag(player).lookInBag()
return@on true
}
on(ALL_COLLECTION_BAGS, IntType.ITEM, "Empty") { player, node ->
resetCollectorBag(player)
return@on true
}
}
fun handleWrongEgg(player: Player, groundItem: GroundItem,point: Boolean = true){
val session = getBASession(player) ?: return
if (point) { session.eventBus.emit(EggCollected(player,-1)) }
impact(player, 5)
val gfx = when (groundItem.id) {
Items.GREEN_EGG_10531 -> GREEN_EGG_BREAK_873
Items.RED_EGG_10532 -> RED_EGG_BREAK_874
Items.BLUE_EGG_10533 -> BLUE_EGG_BREAK_875
else -> 874
}
Graphics.send(Graphics(gfx), player.location)
sendMessage(player,"The egg exploded")
session.destroyGroundItem(groundItem as BAGroundSpawn)
}
fun handleDropItem(player: Player, node: Node, flag: Boolean? = false): Boolean {
val item = node as? Item ?: return false
queueScript(player, strength = QueueStrength.SOFT) {
val current = player.inventory.get(item.slot)
val session = getBASession(player) ?: return@queueScript stopExecuting(player)
if (current == null || current !== item) { return@queueScript stopExecuting(player) }
if (player.inventory.replace(null, item.slot) !== item) { PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential exploit attempt when player ${player.name} tried to drop ${item.amount}x ${item.id}. BA session Items"); return@queueScript stopExecuting(player) }
val droppedItem = item.dropItem
val groundItem = BAGroundSpawn(session,60,droppedItem,player.location,flag)
groundItem.init()
// sendMessage(player, session.BAgroundItems.toString())
setAttribute(player, "droppedItem:${droppedItem.id}", getWorldTicks() + 2)
return@queueScript stopExecuting(player)
}
return true
}
fun playerChangeScenery(player: Player,anim: Int ,node: Node, newSceneryId: Int, pulseDelay: Int = 3, message: String = "") {
player.animate(Animation(anim))
player.lock(pulseDelay)
//if trap send message "You fixed the Penance Runner trap."
Pulser.submit(object : Pulse(pulseDelay, player) {
override fun pulse(): Boolean {
player.unlock()
val scenryNode = node as core.game.node.scenery.Scenery
replaceScenery( scenryNode,newSceneryId ,-1 ,node.location )
sendMessage(player, message)
if ( node.id == Scenery.PENANCE_CAVE_20238) {return true}
removeItem(player,Items.LOGS_11760)
return true
}
})
}
fun sessionDestination(player: Player, node: Node, direction: Direction? = null): Location {
//work around for running off to org location instead of copy location.
//Player Name's Parlor chairs Should fix this!
val session = getBASession(player) ?: return node.location
val borders = getRegionBorders(session.region.regionId)
val base = session.base
return Location.create(
base.x + node.location.x - borders.southWestX + (direction?.stepX ?: 0),
base.y + node.location.y - borders.southWestY + (direction?.stepY ?: 0),
base.z
)
}
private fun hasTools(player: Player): Boolean{
if (getItemFromEquipment(player, EquipmentSlot.CAPE)?.id != DEFENDER_ICON) {return true}
val requiredItems = listOf(Items.HAMMER_2347, Items.LOGS_11760)
for (item in requiredItems) {
if (!inInventory(player, item)) {
sendMessage(player, "You need a ${getItemName(item)}")
return true
}
}
return false
}
fun getHealerHealAmount(player: Player): Int {
val healerHealAmounts = intArrayOf(5, 15, 20, 25, 35)
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)) {
val current = All_HEALING_VIALS.firstOrNull { player.inventory.containsAtLeastOneItem(it) } ?: return@queueScript stopExecuting(player)
player.animate(Animation(5400))
player.inventory.remove(Item(current))
player.inventory.add(Item(Items.HEALING_VIAL4_10542))
sendMessage(player, "You filled the vial.")
}
return@queueScript stopExecuting(player)
}
}
}
object BACombatValidation {
fun resolveAttackerStyle( attacker: Player, state: BattleState ): AttackerStyle? {
return when (state.style) {
CombatStyle.MELEE -> when (attacker.properties.attackStyle.style) {
STYLE_CONTROLLED -> AttackerStyle.STYLE_1
STYLE_ACCURATE -> AttackerStyle.STYLE_2
STYLE_AGGRESSIVE -> AttackerStyle.STYLE_3
STYLE_DEFENSIVE -> AttackerStyle.STYLE_4
else -> null
}
CombatStyle.RANGE -> when (state.ammunition.itemId) {
Items.BRONZE_ARROW_882 -> AttackerStyle.STYLE_1
Items.IRON_ARROW_884 -> AttackerStyle.STYLE_2
Items.STEEL_ARROW_886 -> AttackerStyle.STYLE_3
Items.MITHRIL_ARROW_888 -> AttackerStyle.STYLE_4
else -> null
}
CombatStyle.MAGIC -> when (state.spell.spellId) {
1, 10, 24, 45 -> AttackerStyle.STYLE_1 // Air
4, 14, 27, 48 -> AttackerStyle.STYLE_2 // Water
6, 17, 66, 52 -> AttackerStyle.STYLE_3 // Earth
8, 20, 38, 55 -> AttackerStyle.STYLE_4 // Fire
else -> null
}
else -> null
}
}
fun applyAttackValidation( self: NPC, attacker: Player, state: BattleState ): Int {
val session = getBASession(attacker) ?: return state.estimatedHit
val currentCall = session.roleCalls[BarbRole.COLLECTOR]?.toCall
//todo read my players level instead of scanning the inventory every time for a horn.
val hornBonus = (BAHornSystem.HornHelper.getAttackerHorn(attacker)?.ordinal ?: -1) + 2
val damage = state.estimatedHit + hornBonus
val usedStyle = resolveAttackerStyle(attacker, state)
if (usedStyle == null) { sendMessage(attacker, "That attack is not allowed."); return 0 }
if (usedStyle != currentCall) {
sendMessage(attacker, "Wrong attack style!")
impact(attacker, 1)
session.eventBus.emit(BarbAssEvent.WrongAttack(self, attacker))
return 0
}
return damage
}
}
/*
the game ended early becasue one of your team mates died.
484 = horn of glory!
Barricaded entrances will only slow down, not stop, Penance from entering the battlefield.
Players are allowed to use normal potions; however, potions mixed using Barbarian training may not be brought into the arena.
Every team member will lose points when a runner makes it to the end,
The only combat experience you will get here is the base experience for a magic spell using the runes given to you by the attacker machine.
//You can stand in front of the lure cave to block runners from passing by. (Note that a runner can get through if you are walked on by another player)
[DEFENDER_HORN_10538]
[COLLECTOR_HORN_10560]
Collector Level 1 = [COLLECTION_BAG_10521] - Bag holds 2 eggs
Collector Level 2 = [COLLECTION_BAG_10522] - Bag holds 4 eggs + Egg conversion
Collector Level 3 = [COLLECTION_BAG_10523] - Bag holds 6 eggs + 40% conversion
Collector Level 4 = [COLLECTION_BAG_10524] - Bag holds 7 eggs + 60% conversion
Collector Level 5 = [COLLECTION_BAG_10525] - Bag holds 8 eggs + 80% conversion
Attacker Level 1 = [ATTACKER_HORN_10516] - +1 Damage
Attacker Level 2 = [ATTACKER_HORN_10517] - +2 Damage
Attacker Level 3 = [ATTACKER_HORN_10518] - +3 Damage
Attacker Level 4 = [ATTACKER_HORN_10519] - +4 Damage
Attacker Level 5 = [ATTACKER_HORN_10520] - +5 Damage
Healer Level 1 = [HEALER_HORN_10526] - Heals 10 hp
Healer Level 2 = [HEALER_HORN_10527] - Heals 15 hp
Healer Level 3 = [HEALER_HORN_10528] - Heals 20 hp
Healer Level 4 = [HEALER_HORN_10529] - Heals 25 hp
Healer Level 5 = [HEALER_HORN_10530] - Heals 35 hp
HEALING_VIAL_10546
HEALING_VIAL1_10545
HEALING_VIAL2_10544
HEALING_VIAL3_10543
HEALING_VIAL4_10542
| Level | Honour Points | Defender |
|-------|----------------|----------------|
| 1 | Start level | Lure 4 spaces |
| 2 | 200 | Lure 5 spaces |
| 3 | 300 | Lure 6 spaces |
| 4 | 400 | Lure 8 spaces |
| 5 | 500 | Lure 10 spaces |
|-------|----------------|----------------|
RUNNER_TRAP2_20135
RUNNER_TRAP1_20230
BROKEN_TRAP0_20231
Only normal Magics can damage.
we use these runes in barb assault.
[ATTACKER_ITEM_MACHINE_20241]
Options[ Take-runes, Take-arrows(300 of each)
CATALYTIC_RUNE_12851, ELEMENTAL_RUNE_12850
https://youtu.be/_EipqTK3Qak?t=68
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

@ -0,0 +1,38 @@
package content.minigame.barbassault.arena
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
sealed class BarbAssEvent {
data class NPCDied(val npc: NPC, val killer: Player) : BarbAssEvent()
data class EggCollected(val player: Player, val count: Int) : BarbAssEvent()
data class HitpointsHealed(val player: Player, val amount: Int) : BarbAssEvent()
data class WrongPoisonUsed(val player: Player) : BarbAssEvent()
data class WrongAttack(val self: NPC, val attacker: Player) : BarbAssEvent()
data class RunnerEscaped(val player: Player) : BarbAssEvent()
}
class BarbAssEventBus {
private val listeners = mutableMapOf<Class<out BarbAssEvent>, MutableList<(BarbAssEvent) -> Unit>>()
private val pendingEvents = mutableListOf<BarbAssEvent>()
fun <T : BarbAssEvent> register(eventType: Class<T>, listener: (T) -> Unit) {
listeners.computeIfAbsent(eventType) { mutableListOf() }.add { listener(it as T) }
}
fun emit(event: BarbAssEvent) {
pendingEvents.add(event)
}
fun processEvents() {
for (event in pendingEvents) {
listeners[event::class.java]?.forEach { it.invoke(event) }
}
pendingEvents.clear()
}
fun clear() {
listeners.clear()
}
}

View file

@ -0,0 +1,51 @@
package content.minigame.barbassault.arena
import core.game.node.item.GroundItem
import core.game.node.item.GroundItemManager
import core.game.system.task.Pulse
import core.game.node.item.Item
import core.game.world.GameWorld
import core.game.world.map.Location
class BAGroundSpawn(private var session: BarbassaultSession, private var respawnRate: Int, item: Item?, location: Location?, var flag: Boolean? = null, private var lureRange: Int = 4) : GroundItem(item, location) {
private var active = true
private var autoSpawn = true
override fun toString(): String = "SessionGroundSpawn [name=$name, respawnRate=$respawnRate, loc=$location, flag = $flag]"
fun init(): GroundItem {
val thisGroundItem = GroundItemManager.create(this)
session.BAgroundItems.add(thisGroundItem as BAGroundSpawn)
return thisGroundItem
}
override fun isActive(): Boolean = active
override fun setActive(value: Boolean) { active = value }
override fun isPrivate(): Boolean = false
override fun isAutoSpawn(): Boolean = autoSpawn
fun setAutoSpawn(value: Boolean) { autoSpawn = value }
fun setRespawnRate(rate: Int) { respawnRate = rate }
fun getLureRange():Int = lureRange
fun addLureRange(addRange: Int) { lureRange += addRange }
override fun respawn() {
if (!autoSpawn || respawnRate < 0) {
return
}
GameWorld.Pulser.submit(object : Pulse(respawnRate) {
override fun pulse(): Boolean {
active = true
GroundItemManager.create(this@BAGroundSpawn)
if (!session.BAgroundItems.contains(this@BAGroundSpawn)) {
session.BAgroundItems.add(this@BAGroundSpawn)
}
return true
}
})
}
}

View file

@ -0,0 +1,105 @@
package content.minigame.barbassault.arena
import content.minigame.barbassault.getBASession
import core.api.inInventory
import core.api.sendChat
import core.api.sendMessage
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import org.rs09.consts.Items
class BAHornSystem : InteractionListener {
private val ATTACKER_HORNS = intArrayOf( Items.ATTACKER_HORN_10516, Items.ATTACKER_HORN_10517, Items.ATTACKER_HORN_10518, Items.ATTACKER_HORN_10519, Items.ATTACKER_HORN_10520 )
private val HEALER_HORNS = intArrayOf( Items.HEALER_HORN_10526, Items.HEALER_HORN_10527, Items.HEALER_HORN_10528, Items.HEALER_HORN_10529, Items.HEALER_HORN_10530 )
override fun defineListeners() {
on(Items.DEFENDER_HORN_10538, IntType.ITEM, "Tell-tofu", "Tell-worms", "Tell-meat","Medic", "Drop") { player, _ ->
val optionClicked = player.getAttribute<String>("interact:option")?.lowercase() ?: return@on false
val session = getBASession(player) ?: return@on true
var poisFoodToTell = BarbassaultSession.PoisonFood.TOFU
when (optionClicked) {
"tell-tofu" -> poisFoodToTell = BarbassaultSession.PoisonFood.TOFU
"tell-worms" ->poisFoodToTell = BarbassaultSession.PoisonFood.WORMS
"tell-meat" -> poisFoodToTell = BarbassaultSession.PoisonFood.MEAT
"drop" -> { sendMessage(player, "I need that!");return@on true }
"medic" -> { sendChat(player, "Medic!"); session.alertHealers(player);return@on true }
else -> return@on true
}
sendChat(player, poisFoodToTell.hornShout)
session.onHornCall(player, poisFoodToTell)
true
}
on(Items.COLLECTOR_HORN_10560, IntType.ITEM, "Tell-style1", "Tell-style2", "Tell-style3", "Tell-style4","Medic" , "Drop") { player, _ ->
val optionClicked = player.getAttribute<String>("interact:option")?.lowercase() ?: return@on false
val session = getBASession(player) ?: return@on true
var styleToTell = BarbassaultSession.AttackerStyle.STYLE_1
when (optionClicked){
"tell-style1" -> styleToTell = BarbassaultSession.AttackerStyle.STYLE_1
"tell-style2" -> styleToTell = BarbassaultSession.AttackerStyle.STYLE_2
"tell-style3" -> styleToTell = BarbassaultSession.AttackerStyle.STYLE_3
"tell-style4" -> styleToTell = BarbassaultSession.AttackerStyle.STYLE_4
"drop" -> { sendMessage(player, "I need that!");return@on true }
"medic" -> { sendChat(player, "Medic!"); session.alertHealers(player);return@on true }
}
sendChat(player, styleToTell.hornShout)
session.onHornCall(player, styleToTell)
true
}
on(ATTACKER_HORNS, IntType.ITEM, "Tell-red", "Tell-green", "Tell-blue", "Medic", "Drop") { player, _ ->
val optionClicked = player.getAttribute<String>("interact:option")?.lowercase() ?: return@on false
val session = getBASession(player) ?: return@on true
var eggToTell = BarbassaultSession.EggColor.RED
when (optionClicked) {
"tell-red" -> eggToTell = BarbassaultSession.EggColor.RED
"tell-green" -> eggToTell = BarbassaultSession.EggColor.GREEN
"tell-blue" -> eggToTell = BarbassaultSession.EggColor.BLUE
"drop" -> { sendMessage(player, "I need that!");return@on true }
"medic" -> { sendChat(player, "Medic!"); session.alertHealers(player);return@on true }
}
sendChat(player, eggToTell.hornShout)
session.onHornCall(player, eggToTell)
true
}
on(HEALER_HORNS, IntType.ITEM, "Tell-tofu", "Tell-crackers", "Tell-worms","Medic", "Drop") { player, _ ->
val optionClicked = player.getAttribute<String>("interact:option")?.lowercase() ?: return@on false
val session = getBASession(player) ?: return@on true
var lureFoodToTell = BarbassaultSession.LureFood.TOFU
when (optionClicked){
"tell-tofu" -> lureFoodToTell = BarbassaultSession.LureFood.TOFU
"tell-crackers" -> lureFoodToTell = BarbassaultSession.LureFood.CRACKERS
"tell-worms" -> lureFoodToTell = BarbassaultSession.LureFood.WORMS
"drop" -> { sendMessage(player, "I need that!");return@on true }
"medic" -> { sendChat(player, "Medic!"); session.alertHealers(player);return@on true }
}
sendChat(player, lureFoodToTell.hornShout)
session.onHornCall(player, lureFoodToTell)
true
}
}
object HornHelper {
//I should set a value in my players session instead of scanning the inventory every time for a horn.
//multiple horn IDs suggest retail would use a gearSet check to apply a damage modifier.
fun getAttackerHorn(player: Player): AttackerHorn? = AttackerHorn.values().firstOrNull { inInventory(player, it.itemId) }
}
enum class AttackerHorn(val itemId: Int) {
LEVEL_ONE(Items.ATTACKER_HORN_10516),
LEVEL_TWO(Items.ATTACKER_HORN_10517),
LEVEL_THREE(Items.ATTACKER_HORN_10518),
LEVEL_FOUR(Items.ATTACKER_HORN_10519),
LEVEL_FIVE(Items.ATTACKER_HORN_10520);
companion object {
private val map = values().associateBy(AttackerHorn::itemId)
fun fromId(id: Int): AttackerHorn? = map[id]
}
}
}

View file

@ -0,0 +1,112 @@
package content.minigame.barbassault.arena
import core.game.node.entity.player.Player
//https://www.youtube.com/watch?v=OJ2BDEM03Dw
class BarbScoreManager {
private val trackers = mutableMapOf<Player, ScoreTracker>()
fun trackerFor(player: Player): ScoreTracker = trackers.getOrPut(player) { ScoreTracker() }
fun resetAll() {
trackers.values.forEach { it.reset() }
}
fun remove(player: Player) {
trackers.remove(player)
}
fun allTrackers(): Collection<ScoreTracker> = trackers.values
fun personal(player: Player, caps: SpawnCaps): RoleScore {
return trackerFor(player).toScoreboard(caps)
}
//val caps = session.currentWaveCaps()
//val teamScore = session.scoreManager.team(caps)
fun team(caps: SpawnCaps): RoleScore {
var total = RoleScore.ZERO
for (tracker in allTrackers()) {
total = total.add(tracker.toScoreboard(caps))
}
return total
}
}
data class AttackerStats(var rangersKilled: Int = 0, var fightersKilled: Int = 0, var incorrectStyleAttacks: Int = 0)
data class CollectorStats(var eggsCollected: Int = 0, var eggsExploded: Int = 0) { val netEggs: Int get() = eggsCollected - eggsExploded }
data class DefenderStats(var runnersKilled: Int = 0, var runnersPast: Int = 0)
data class HealerStats(var healersKilled: Int = 0, var hitpointsReplenished: Int = 0, var wrongPoisonUsed: Int = 0)
data class ScoreTracker(var attackerStats: AttackerStats = AttackerStats(), var collectorStats: CollectorStats = CollectorStats(),
var defenderStats: DefenderStats = DefenderStats(),var healerStats: HealerStats = HealerStats())
{
fun reset() {
attackerStats = AttackerStats()
collectorStats = CollectorStats()
defenderStats = DefenderStats()
healerStats = HealerStats()
}
fun toScoreboard(caps: SpawnCaps): RoleScore = BarbAssaultScoreBuilder.build(this, caps)
}
data class RoleScore(val rangersKilled: Int, val fightersKilled: Int, val incorrectStylePenalty: Int,
val eggsScore: Int,
val runnersKilledScore: Int, val runnersPastPenalty: Int,
val healersKilledScore: Int, val hitpointsHealedScore: Int, val wrongPoisonPenalty: Int )
{
val attackerTotal: Int get() = rangersKilled + fightersKilled + incorrectStylePenalty
val collectorTotal: Int get() = eggsScore
val defenderTotal: Int get() = runnersKilledScore + runnersPastPenalty
val healerTotal: Int get() = healersKilledScore + hitpointsHealedScore + wrongPoisonPenalty
fun add(other: RoleScore): RoleScore = RoleScore(
rangersKilled + other.rangersKilled,
fightersKilled + other.fightersKilled,
incorrectStylePenalty + other.incorrectStylePenalty,
eggsScore + other.eggsScore,
runnersKilledScore + other.runnersKilledScore,
runnersPastPenalty + other.runnersPastPenalty,
healersKilledScore + other.healersKilledScore,
hitpointsHealedScore + other.hitpointsHealedScore,
wrongPoisonPenalty + other.wrongPoisonPenalty
)
companion object {
val ZERO = RoleScore(0,0,0,0,0,0,0,0,0)
}
}
object BarbAssaultScoreBuilder {
fun build(tracker: ScoreTracker, caps: SpawnCaps): RoleScore {
val atk = tracker.attackerStats
val col = tracker.collectorStats
val def = tracker.defenderStats
val heal = tracker.healerStats
val rangerKillScore = atk.rangersKilled.coerceAtMost(caps.rangers) - 1
val fighterKillScore = atk.fightersKilled.coerceAtMost(caps.fighters) - 1
val incorrectStylePenalty = (-atk.incorrectStyleAttacks + 1).coerceAtLeast(-10)
val eggScore = (col.netEggs.coerceAtMost(60) / 4.35).toInt()
val runnersKilledScore = (1.8 * def.runnersKilled.coerceAtMost(caps.runners)).toInt()
val runnersPastPenalty = (-3 * def.runnersPast).coerceAtLeast(-10)
val healerKillScore = (0.7 * heal.healersKilled.coerceAtMost(caps.healers)).toInt()
val hitpointsHealedScore = (heal.hitpointsReplenished / 18).coerceAtMost(28)
val wrongPoisonPenalty = -(heal.wrongPoisonUsed.coerceAtMost(512) / 4)
return RoleScore(
rangersKilled = rangerKillScore,
fightersKilled = fighterKillScore,
incorrectStylePenalty = incorrectStylePenalty,
eggsScore = eggScore,
runnersKilledScore = runnersKilledScore,
runnersPastPenalty = runnersPastPenalty,
healersKilledScore = healerKillScore,
hitpointsHealedScore = hitpointsHealedScore,
wrongPoisonPenalty = wrongPoisonPenalty
)
}
}

View file

@ -0,0 +1,687 @@
package content.minigame.barbassault.arena
import content.minigame.barbassault.ALL_BARBASS_ITEMS
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.BAActivity
import content.minigame.barbassault.LureItems
import content.minigame.barbassault.arena.scenery.EggCannon
import content.minigame.barbassault.arena.scenery.GloryHorn
import content.minigame.barbassault.getBALevels
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.getBAWave
import content.minigame.barbassault.lobby.*
import content.minigame.barbassault.lobby.BALobby.Companion.waveLobbies
import content.minigame.barbassault.setBASession
import core.api.*
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_ACCURATE
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_AGGRESSIVE
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_CONTROLLED
import core.game.node.entity.combat.equipment.WeaponInterface.STYLE_DEFENSIVE
import core.game.node.entity.impl.PulseManager
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.GroundItemManager
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.GameWorld.Pulser
import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.build.DynamicRegion
import core.game.world.map.zone.*
import core.tools.Log
import core.tools.secondsToTicks
import core.tools.ticksToSeconds
import org.rs09.consts.Components
import org.rs09.consts.Items
import org.rs09.consts.Music.ASSAULT_AND_BATTERY_604
import org.rs09.consts.NPCs
import removeCollectorBag
import kotlin.text.lowercase
enum class BarbassaultState { PARTY_CREATION, STARTING, IN_ARENA,FORFEIT, COMPLETE,DEATH, END }
data class PenanceNpcTunnel( val type: PenanceType, val caveLocation: Location, val spawnOffset: Location, val direction: Direction)
data class ArenaSpawnItems(val id: Int, val location: Location)
class BarbassaultSession( val activity: BAActivity ? = null) :LogoutListener, MapArea {
lateinit var region: DynamicRegion
lateinit var base: Location
lateinit var currentWaveDefinition: WaveDefinition
lateinit var team: BarbTeam
var state: BarbassaultState = BarbassaultState.PARTY_CREATION
val sessionNpcs = mutableListOf<NPC>()
var currentNPCCount = SpawnCaps(0,0,0,0)
var currentNPCDeathCount = SpawnCaps(0,0,0,0)
var currentNPCEscapeCount = SpawnCaps(0,0,0,0)
var npcTunnel: List<PenanceNpcTunnel> = listOf(
PenanceNpcTunnel(PenanceType.RANGER,location(17,47,0),location(18,46,0),Direction.SOUTH),
PenanceNpcTunnel(PenanceType.FIGHTER,location(23,48,0),location(24,47,0),Direction.SOUTH),
PenanceNpcTunnel(PenanceType.RUNNER,location(35,48,0),location(36,47,0),Direction.SOUTH),
PenanceNpcTunnel(PenanceType.HEALER,location(41,47,0),location(42,46,0),Direction.SOUTH))
private val caveBlocked = mutableMapOf<PenanceType, Boolean>()
val BAgroundItems = mutableListOf<BAGroundSpawn>()
val arenaSpawnItems: List<ArenaSpawnItems> = listOf(ArenaSpawnItems(Items.HAMMER_2347,location(32,42,0)),ArenaSpawnItems(Items.LOGS_11760,location(30,46,0)),ArenaSpawnItems(Items.LOGS_11760,location(29,47,0)))
val eggHopper = EggCannon.EggHopper()
var eggCannonNpcWest: NPC? = null
var eggCannonNpcEast: NPC? = null
val scoreManager = BarbScoreManager()
val eventBus = BarbAssEventBus()
private var countDoown = if(GameWorld.settings?.isDevMode == true) 10 else 30
private var countdownPulse: Pulse? = null
fun beginStartingPhase(quickStart: Boolean = false) {
if (state != BarbassaultState.PARTY_CREATION) return
state = BarbassaultState.STARTING
currentWaveDefinition = getWaveDefinition(team.wave)
createArena()
startCountdown(quickStart)
}
fun cancleStartingPhase(){
if (state != BarbassaultState.STARTING) return
state = BarbassaultState.PARTY_CREATION
countdownPulse?.stop()
countdownPulse = null
broadcast("-- Next wave no longer starting")
}
private fun enterArena() {
state = BarbassaultState.IN_ARENA
region.setMusicId(ASSAULT_AND_BATTERY_604)
for (player in team.allPlayers()) {
val pRole = getBASession(player)?.team?.getRoleForPlayer(player)
equipBARoleIcon(player, pRole!!)
player.properties.teleportLocation = team.spawnLocationFor(player)
giveRoleItems(player,pRole)
registerLogoutListener(player, "ba-logout") { handleLogout(player) }
}
Pulser.submit(GamePulse())
tryGroundItemSpawn()
}
fun endSession() {
if (state == BarbassaultState.END) return
state = BarbassaultState.END
for (player in team.allPlayers()) {
clearLogoutListener(player, "ba-logout")
player.removeAttribute("ba-session")
player.properties.teleportLocation = Location.create(2593, 5264, 0)
player.interfaceManager.closeOverlay()
}
// https://www.youtube.com/watch?v=vYKbHNZn80w
destroyArena()
}
override fun logout(player: Player) {
if (!defineAreaBorders().any { it.insideBorder(player.location) }) {
return
}
player.properties.teleportLocation = Location.create(2593, 5264, 0)
}
private fun startCountdown(quickStart: Boolean) {
countDoown = if (quickStart) 1 else countDoown
var ticks = secondsToTicks(countDoown)//30
if(!quickStart){ team.allPlayers().forEach { openOverlay(it, 494);setInterfaceText(it, "30", 494, 0) } }
countdownPulse = object : Pulse(1) {
override fun pulse(): Boolean {
if (state != BarbassaultState.STARTING) return true
ticks--
if (ticks <= 0) {
enterArena()
rollNewRoleCalls()
return true
}
val secondsLeft = ticksToSeconds(ticks)
val display = secondsLeft.toString().padStart(2, '0')
if(!quickStart) {
team.allPlayers().forEach { setInterfaceText(it, display, 494, 0) }
}
return false
}
}
Pulser.submit(countdownPulse!!)
}
private fun InitEvents() {
eventBus.register(BarbAssEvent.NPCDied::class.java) { event ->
val tracker = scoreManager.trackerFor(event.killer)
val npcRoleType = event.npc.getAttribute<BarbRole>("barbass-role")
when (npcRoleType) {
BarbRole.ATTACKER -> tracker.attackerStats.rangersKilled++
BarbRole.DEFENDER -> tracker.defenderStats.runnersKilled++
BarbRole.HEALER -> tracker.healerStats.healersKilled++
else -> {}
}
}
eventBus.register(BarbAssEvent.EggCollected::class.java) { event ->
val tracker = scoreManager.trackerFor(event.player)
tracker.collectorStats.eggsCollected += event.count
}
eventBus.register(BarbAssEvent.HitpointsHealed::class.java) { event ->
val tracker = scoreManager.trackerFor(event.player)
tracker.healerStats.hitpointsReplenished += event.amount
}
eventBus.register(BarbAssEvent.WrongPoisonUsed::class.java) { event ->
val tracker = scoreManager.trackerFor(event.player)
tracker.healerStats.wrongPoisonUsed++
}
eventBus.register(BarbAssEvent.RunnerEscaped::class.java) { event ->
val tracker = scoreManager.trackerFor(event.player)
tracker.defenderStats.runnersPast++
}
}
fun isPenanceDead(type: PenanceType): Boolean = currentNPCDeathCount[type] >= currentWaveDefinition.caps[type]
fun isAllPenanceDead(): Boolean = PenanceType.values().all { isPenanceDead(it) }
fun runnerEscaped(runner: NPC) {
val defenders = team.getPlayersByRole(BarbRole.DEFENDER)
val penalizedPlayers = if (defenders.isEmpty()) team.allPlayers() else defenders
penalizedPlayers.forEach { eventBus.emit(BarbAssEvent.RunnerEscaped(it)) }
val spawn = npcTunnel.firstOrNull { it.type == PenanceType.RUNNER } ?: return
runner.resetWalk()
runner.teleport(base.transform(spawn.spawnOffset))
}
fun runnerKilledByTrap(runner: NPC) {
sessionNpcs.remove(runner)
pennanceClearedMessage(runner)
}
fun pennanceClearedMessage(npc: NPC) {
val session = getBASession(npc) ?: return
if (npc.getAttribute("ba:npc-death-counted", false)) {
return
}
npc.setAttribute("ba:npc-death-counted", true)
val type = npc.getAttribute<PenanceType>("barbass-type")
session.currentNPCDeathCount[type]++
if (session.isPenanceDead(type)) {
type.toString()
session.broadcast("All of the Penance ${type.name.lowercase().replaceFirstChar { it.uppercase() }}s have been killed!")
}
}
fun setCaveBlocked(node: Node, blocked: Boolean) {
val tunnel = npcTunnel.firstOrNull { t -> base.transform(t.caveLocation) == node.location } ?: return
if (blocked) {
caveBlocked[tunnel.type] = true
} else {
caveBlocked.remove(tunnel.type)
}
}
fun isCaveBlocked(type: PenanceType): Boolean {
return caveBlocked[type] == true
}
fun forceWaveSpawn(force: Boolean = false){
tryPenanceSpawn(PenanceType.RANGER,force)
tryPenanceSpawn(PenanceType.FIGHTER,force)
tryPenanceSpawn(PenanceType.RUNNER,force)
tryPenanceSpawn(PenanceType.HEALER,force)
}
fun tryPenanceSpawn(type: PenanceType, force: Boolean = false): Boolean {
val cap = currentWaveDefinition.caps[type]
val npcId = currentWaveDefinition.npcIds[type] ?: return false
if (currentNPCDeathCount[type] >= cap) return false
val current = currentNPCCount[type]
if (!force && current >= cap) return false
val spawn = npcTunnel.firstOrNull { it.type == type } ?: return false
val npc = NPC.create(npcId, base.transform(location(30, 31, 0)), spawn.direction) ?: return false
setBASession(npc as Entity,this)
npc.location = base.transform(spawn.spawnOffset)
npc.init()
sessionNpcs.add(npc)
currentNPCCount[type]++
return isCaveBlocked(type)
}
fun tryGroundItemSpawn(){
val groundItems = GroundItemManager.getItems()
arenaSpawnItems.forEach { spawnItem ->
val spawnLoc = spawnItem.location
val alreadyExists = groundItems.any { it.id == spawnItem.id && it.location == spawnLoc && !it.isRemoved }
if (!alreadyExists) {
produceGroundSpawnItem(6,Item(spawnItem.id, 1),spawnLoc)
}
}
}
fun pickUpGroundItem(player: Player,node:BAGroundSpawn): Boolean {
if (player.inventory.add(node)) {
destroyGroundItem(node, allowRespawn = isArenaSpawnItem(node))
return true
}
sendMessage(player,"You don't have enough inventory space to hold that item.")
return false
}
fun destroyGroundItem(node: BAGroundSpawn, allowRespawn: Boolean = false){
node.isActive = false
if (!allowRespawn) {
node.isAutoSpawn = false
BAgroundItems.remove(node)
}
GroundItemManager.destroy(node)
}
private fun isArenaSpawnItem(node: BAGroundSpawn): Boolean {
return arenaSpawnItems.any { spawnItem ->
node.id == spawnItem.id && node.location == base.transform(spawnItem.location)
}
}
fun findClosestLure(target: Location): BAGroundSpawn? {
fun distance(a: Location, b: Location): Int =Math.abs(a.x - b.x) + Math.abs(a.y - b.y)
return BAgroundItems
.asSequence()
.map { it to distance(it.location, target) }
.filter { (node, dist) -> node.id in LureItems && dist <= node.getLureRange() }
.minByOrNull { (_, dist) -> dist }?.first
}
fun forfeit(){ state = BarbassaultState.FORFEIT }
fun isInBarbAssaultGame() = state == BarbassaultState.IN_ARENA
fun endGame(endReason: String){
eventBus.clear()
scoreManager.resetAll()
BALobby.TeamToLobby(team,team.wave)
state = BarbassaultState.PARTY_CREATION
for(player in team.allPlayers()) {
PulseManager.cancelDeathTask(player)
Pulser.submit(object : Pulse(1, player) {
override fun pulse(): Boolean {
player.getSkills().restore()
return true
}
})
closeInterface(player)
openOverlay(player, 256)
//https://youtu.be/MpYZG3qkFUA?t=387
//todo do not send message to person who exited the game.
player.dialogueInterpreter.sendDialogue("The game ended early because one of your team-mates $endReason.")
BarbTeamManager.updateTeam(getBASession(player)?.team!!)
}
}
//MAIN GAME LOOP
private inner class GamePulse : Pulse(1) {
private val baseCallTicks = secondsToTicks(30)
private var roleCallTicks = baseCallTicks
private var baseSpawnTicks = secondsToTicks(6)
private var waveSpawnTicks = SpawnCaps( baseSpawnTicks, baseSpawnTicks, baseSpawnTicks, baseSpawnTicks )
override fun pulse(): Boolean {
when(state){
BarbassaultState.IN_ARENA -> {
eventBus.processEvents()
roleCallTicks--
if (roleCallTicks <= 0) { rollNewRoleCalls(); roleCallTicks = baseCallTicks }
for (type in PenanceType.values()) {
waveSpawnTicks[type]--
if (waveSpawnTicks[type] <= 0) { waveSpawnTicks[type] = if (tryPenanceSpawn(type)) baseSpawnTicks * 2 else baseSpawnTicks
}
}
}
BarbassaultState.FORFEIT -> { endGame("exited early."); return true }
BarbassaultState.COMPLETE -> {
log(this.javaClass, Log.FINE,state.toString())
BALobby.TeamToLobby(team,team.wave+1)
state = BarbassaultState.PARTY_CREATION
for(player in team.allPlayers()) {
PulseManager.cancelDeathTask(player)
closeInterface(player)
openOverlay(player, 256)
BarbTeamManager.updateTeam(getBASession(player)?.team!!)
}
return true // we do not have auto start next wave in 2009
}
BarbassaultState.DEATH -> { endGame("died."); return true }
BarbassaultState.END -> return true
else -> return false
}
return false
}
}
private fun createArena() {
region = DynamicRegion.create(BarbassaultArenaListener.BA_ARENA.MAIN)
base = region.baseLocation
zone.zoneType = ZoneType.SAFE.id
region.regionZones.add(RegionZone(activity!!, region.borders)) //DONT FORGET THIS STUPID!
region.toggleMulticombat()
spawnEggCannons()
zone.register(getRegionBorders(region.id))
}
private fun destroyArena() {
if (!this::region.isInitialized) return
zone.unregister(getRegionBorders(region.id))
region.clear()
region.flagInactive()
}
private fun handleLogout(player: Player) {
state = BarbassaultState.FORFEIT
zone.cleanItems(player,ALL_BARBASS_ITEMS)
region.remove(player)
team.removePlayer(player)
player.locks.unlockTeleport()
player.location = waveLobbies[team.wave].randomWalkableLoc
/* if (team.allPlayers().isEmpty()) { // might be redundant
player.properties.teleportLocation = Location.create(2593, 5264, 0)
endSession()
}*/
}
override fun defineAreaBorders(): Array<ZoneBorders> = arrayOf()
override fun getRestrictions(): Array<ZoneRestriction> = arrayOf( ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.TELEPORT )
override fun areaEnter(entity: Entity) {
val player = entity as? Player ?: return
log(this::class.java, Log.FINE, "ENTERED Barbassault Arena")
val waveNum = getBAWave(player)
val interfaceId = getRoleInterfaceId(getBASession(player)!!.team.getRoleForPlayer(player))
player.fullRestore()
//todo consolidate logic.
interfaceId?.let {
sendMessage(player, "----- Wave: $waveNum -----")
openOverlay(player, it)
}
}
override fun areaLeave(entity: Entity, logout: Boolean) {
super.areaLeave(entity, logout)
val player = entity as? Player ?: return
log(this::class.java, Log.FINE, "LEFT Barbassault Arena")
removeTimer(player, "teleblock")
zone.cleanItems(player,ALL_BARBASS_ITEMS)
removeCollectorBag(player)
clearLogoutListener(player, "ba-logout")
player.fullRestore()
destroyArena()
}
fun sessionNPCSpawn(id: Int, location: Location, direction: Direction, vararg objects: Any): NPC? = NPC.create(id, base.transform(location), direction, *objects)?.also { it.init(); sessionNpcs.add(it) }
fun produceGroundSpawnItem(rate:Int,item: Item,location: Location): BAGroundSpawn = BAGroundSpawn(this, rate, item, base.transform(location)).also { it.init() }
fun spawnEggCannons() {
eggCannonNpcWest = sessionNPCSpawn( NPCs.EGG_LAUNCHER_5026, location(21, 34, 0), Direction.NORTH )
if (team.wave == 10) return
eggCannonNpcEast = sessionNPCSpawn( NPCs.EGG_LAUNCHER_5026, location(40, 34, 0), Direction.NORTH )
}
fun clearGroundItems(){
BAgroundItems.forEach { item ->
item.isActive = false
item.setRespawnRate(-1)
item.isAutoSpawn = false
GroundItemManager.destroy(item)
}
}
private fun giveRoleItems(player: Player, role: BarbRole) {
val attackerHorns = intArrayOf(Items.ATTACKER_HORN_10516,Items.ATTACKER_HORN_10517,Items.ATTACKER_HORN_10518,Items.ATTACKER_HORN_10519,Items.ATTACKER_HORN_10520)
val healerHorns = intArrayOf(Items.HEALER_HORN_10526,Items.HEALER_HORN_10527,Items.HEALER_HORN_10528,Items.HEALER_HORN_10529,Items.HEALER_HORN_10530)
val collectorBags = intArrayOf(Items.COLLECTION_BAG_10521,Items.COLLECTION_BAG_10522,Items.COLLECTION_BAG_10523,Items.COLLECTION_BAG_10524,Items.COLLECTION_BAG_10525)
fun levelIndex(level: Int): Int { return (level.coerceIn(1, 5) - 1) }
val levels = getBALevels(player)
val items = when (role) {
BarbRole.ATTACKER -> intArrayOf( attackerHorns[levelIndex(levels.atk)] )
BarbRole.DEFENDER -> intArrayOf( Items.DEFENDER_HORN_10538 )
BarbRole.HEALER -> intArrayOf( healerHorns[levelIndex(levels.heal)] )
BarbRole.COLLECTOR -> intArrayOf( collectorBags[levelIndex(levels.col)], Items.COLLECTOR_HORN_10560 )
}
items.forEach { player.inventory.add(Item(it, 1)) }
}
private fun equipBARoleIcon(player: Player, role: BarbRole) {
val BA_ROLE_CAPE = mapOf( BarbRole.ATTACKER to 10556, BarbRole.DEFENDER to 10558, BarbRole.COLLECTOR to 10557, BarbRole.HEALER to 10559 )
val itemId = BA_ROLE_CAPE[role] ?: return
player.equipment.add(Item(itemId),true,false)
}
fun broadcast(message: String) = team.allPlayers().forEach { sendMessage(it, message) }
fun broadcastToRole(role: BarbRole, message: String) = team.getPlayersByRole(role).forEach { sendMessage(it, message) }
fun updateBarbHealerIfaceHPOfPlayer(player: Player) = team.getPlayersByRole(BarbRole.HEALER).forEach { HealerIfaceHPUpdatePlayer(it, player)}
fun alertHealers(player:Player) = team.getPlayersByRole(BarbRole.HEALER).forEach { HealerIfaceMedicHelp(it,player) }
fun updateAttackerUI(player: Player, call: AttackerCall) {
val style = call.called
if (style == null) {
setInterfaceText(player, "- - -", Components.BARBASSAULT_OVER_ATT_485, 4)
setInterfaceText(player, "", Components.BARBASSAULT_OVER_ATT_485, 5)
return
}
setInterfaceText(player, style.top, Components.BARBASSAULT_OVER_ATT_485, 4)
setInterfaceText(player, style.bottom, Components.BARBASSAULT_OVER_ATT_485, 5)
}
fun updateOtherRoleUI(player: Player,call: RoleCall<*>,interfaceId: Int,childId: Int ) {
val text = (call.called as? HasUIText)?.ui ?: "- - -"
setInterfaceText(player, text, interfaceId, childId)
}
fun updateRoleUI() {
team.allPlayers().forEach { player ->
val wave = team.wave.toString()
val role = team.getRoleForPlayer(player)
val call = roleCalls[role] ?: return@forEach
when (role) {
BarbRole.ATTACKER -> {
updateAttackerUI(player, call as AttackerCall)
setInterfaceText(player, "Wave $wave" , Components.BARBASSAULT_OVER_ATT_485, 2)
setInterfaceText(player, call.crossRoleDisplay(), Components.BARBASSAULT_OVER_ATT_485, 7)
}
BarbRole.COLLECTOR -> {
updateOtherRoleUI(player, call, Components.BARBASSAULT_OVER_COL_486, 4)
setInterfaceText(player, "Wave $wave" , Components.BARBASSAULT_OVER_COL_486, 2)
setInterfaceText(player, call.crossRoleDisplay(), Components.BARBASSAULT_OVER_COL_486, 6)
}
BarbRole.DEFENDER -> {
updateOtherRoleUI(player, call, Components.BARBASSAULT_OVER_DEF_487, 3)
setInterfaceText(player, "Wave $wave" , Components.BARBASSAULT_OVER_DEF_487, 1)
setInterfaceText(player, call.crossRoleDisplay(), Components.BARBASSAULT_OVER_DEF_487, 5)
}
BarbRole.HEALER -> {
updateOtherRoleUI(player, call, Components.BARBASSAULT_OVER_HEAL_488, 31)
setInterfaceText(player, "Wave $wave" , Components.BARBASSAULT_OVER_HEAL_488, 29)
setInterfaceText(player, call.crossRoleDisplay(), Components.BARBASSAULT_OVER_HEAL_488, 33)
}
}
}
}
fun getRoleInterfaceId(role: BarbRole): Int? {
return when (role) {
BarbRole.ATTACKER -> Components.BARBASSAULT_OVER_ATT_485
BarbRole.COLLECTOR -> Components.BARBASSAULT_OVER_COL_486
BarbRole.DEFENDER -> Components.BARBASSAULT_OVER_DEF_487
BarbRole.HEALER -> Components.BARBASSAULT_OVER_HEAL_488
}
}
fun rollNewRoleCalls() {
roleCalls[BarbRole.ATTACKER] = AttackerCall(AttackerStyle.values().random())
roleCalls[BarbRole.HEALER] = HealerCall(PoisonFood.values().random())
roleCalls[BarbRole.DEFENDER] = DefenderCall(LureFood.values().random())
roleCalls[BarbRole.COLLECTOR] = CollectorCall(EggColor.values().random())
roleCalls[BarbRole.COLLECTOR]?.toCall = roleCalls[BarbRole.ATTACKER]?.required
roleCalls[BarbRole.ATTACKER]?.toCall = roleCalls[BarbRole.COLLECTOR]?.required
roleCalls[BarbRole.HEALER]?.toCall = roleCalls[BarbRole.DEFENDER]?.required
roleCalls[BarbRole.DEFENDER]?.toCall = roleCalls[BarbRole.HEALER]?.required
updateRoleUI()
updateGloryHornUI()
team.allPlayers().forEach { if(it.isArtificial){ botMakeCall(it) } }
}
fun updateGloryHornUI(){
team.allPlayers().forEach { player ->
val atkCallText = (roleCalls[BarbRole.COLLECTOR]?.toCall as? AttackerStyle)?.let { "${it.top}${it.bottom}" } ?: GloryHorn.CallOut.defaultText
val colCallText = (roleCalls[BarbRole.ATTACKER]?.toCall as? HasUIText)?.ui ?: GloryHorn.CallOut.defaultText
val defCallText = (roleCalls[BarbRole.HEALER]?.toCall as? HasUIText)?.ui ?: GloryHorn.CallOut.defaultText
val healCallText = (roleCalls[BarbRole.DEFENDER]?.toCall as? HasUIText)?.ui ?: GloryHorn.CallOut.defaultText
setInterfaceText(player, atkCallText, Components.BARBASSAULT_HORN_484, GloryHorn.CallOut.attacker)
setInterfaceText(player, colCallText, Components.BARBASSAULT_HORN_484, GloryHorn.CallOut.collector)
setInterfaceText(player, defCallText, Components.BARBASSAULT_HORN_484, GloryHorn.CallOut.defender)
setInterfaceText(player, healCallText, Components.BARBASSAULT_HORN_484, GloryHorn.CallOut.healer)
}
}
private val hornTargets = mapOf( BarbRole.COLLECTOR to BarbRole.ATTACKER, BarbRole.ATTACKER to BarbRole.COLLECTOR ,
BarbRole.HEALER to BarbRole.DEFENDER, BarbRole.DEFENDER to BarbRole.HEALER )
private fun dispatchCall(call: Any, calledValue: Any) {
when (call) {
is AttackerCall -> if (calledValue is AttackerStyle) call.call(calledValue)
is HealerCall -> if (calledValue is PoisonFood) call.call(calledValue)
is DefenderCall -> if (calledValue is LureFood) call.call(calledValue)
is CollectorCall -> if (calledValue is EggColor) call.call(calledValue)
}
}
fun onHornCall(player: Player, calledValue: Any) {
val callerRole = team.getRoleForPlayer(player)
val targetRole = hornTargets[callerRole] ?: return
val call = roleCalls[targetRole] ?: return
dispatchCall(call,calledValue)
updateRoleUI()
}
fun botMakeCall(bot: Player) {
val session = getBASession(bot) ?: return
val role = session.team.getRoleForPlayer(bot)
val calledValue = when (role) {
BarbRole.ATTACKER -> session.roleCalls[BarbRole.ATTACKER]?.toCall as? EggColor
BarbRole.COLLECTOR -> session.roleCalls[BarbRole.COLLECTOR]?.toCall as? AttackerStyle
BarbRole.DEFENDER -> session.roleCalls[BarbRole.DEFENDER]?.toCall as? PoisonFood
BarbRole.HEALER -> session.roleCalls[BarbRole.HEALER]?.toCall as? LureFood
} ?: return
session.onHornCall(bot, calledValue)
val text = when (calledValue) {
is EggColor -> calledValue.hornShout
is PoisonFood -> calledValue.hornShout
is LureFood -> calledValue.hornShout
is AttackerStyle -> calledValue.hornShout
else -> return
}
sendChat(bot, text)
}
fun onHornGloryCall(calledValue: Any) {
val targetRole = when (calledValue) {
is AttackerStyle -> BarbRole.ATTACKER
is PoisonFood -> BarbRole.HEALER
is LureFood -> BarbRole.DEFENDER
is EggColor -> BarbRole.COLLECTOR
else -> return
}
val call = roleCalls[targetRole] ?: return
dispatchCall(call,calledValue)
updateRoleUI()
}
fun onAttackerHit(style: AttackerStyle) {
val call = roleCalls[BarbRole.ATTACKER] as? AttackerCall ?: return
call.check(style)
}
val roleCalls: MutableMap<BarbRole, RoleCall<*>> = mutableMapOf()
interface HasUIText { val ui: String; val hornShout:String }
enum class AttackerStyle(val top: String, val bottom: String, override val hornShout:String,val attackStyle: Int):HasUIText {
STYLE_1("Controlled/","Bronze/Wind", "Attacker: Controlled/Bronze/Wind!",STYLE_CONTROLLED),
STYLE_2("Accurate/","Iron/Water", "Attacker: Accurate/Iron/Water!", STYLE_ACCURATE),
STYLE_3("Aggressive/","Steel/Earth", "Attacker: Aggressive/Steel/Earth!",STYLE_AGGRESSIVE),
STYLE_4("Defensive/","Mithril/Fire", "Attacker: Defensive/Mithril/Fire!",STYLE_DEFENSIVE);
override val ui: String get() = top
}
enum class EggColor(override val ui: String, val itemId:Int, override val hornShout:String):HasUIText {
RED( "Red Eggs", Items.RED_EGG_10532, "Collector: Red Eggs!"),
GREEN("Green Eggs",Items.GREEN_EGG_10531, "Collector: Green Eggs!"),
BLUE( "Blue Eggs", Items.BLUE_EGG_10533, "Collector: Blue Eggs!")
}
enum class PoisonFood(override val ui: String, val itemId:Int, override val hornShout:String):HasUIText {
TOFU( "Pois. Tofu", Items.POISONED_TOFU_10539, "Healer: Poison tofu!"),
WORMS("Pois. Worms",Items.POISONED_WORMS_10540,"Healer: Poison worms!"),
MEAT( "Pois. Meat", Items.POISONED_MEAT_10541, "Healer: Poison meat!");
}
enum class LureFood(override val ui: String, val itemId:Int, override val hornShout:String):HasUIText {
TOFU( "Tofu", Items.TOFU_10514, "Defender: Drop Tofu!"),
CRACKERS("Crackers",Items.CRACKERS_10513,"Defender: Drop Crackers!"),
WORMS( "Worms", Items.WORMS_10515, "Defender: Drop Worms!");
}
class AttackerCall( override val required: AttackerStyle ) : RoleCall<AttackerStyle>()
class HealerCall( override val required: PoisonFood ) : RoleCall<PoisonFood>()
class DefenderCall( override val required: LureFood ) : RoleCall<LureFood>()
class CollectorCall( override val required: EggColor ) : RoleCall<EggColor>()
sealed class RoleCall<T> {
abstract val required: T
var called: T? = null
private set
var succeeded: Boolean = false
private set
var toCall: Any? = null
fun call(value: T) { called = value }
fun check(value: T): Boolean { if (value == required) { succeeded = true; return true }; return false }
fun crossRoleDisplay(): String {
return when (this) {
is CollectorCall -> (toCall as? AttackerStyle)?.name?.lowercase()?.replaceFirstChar { it.uppercase() } ?: "- - -"
else -> (toCall as? HasUIText)?.ui ?: "- - -"
}
}
}
}

View file

@ -0,0 +1,51 @@
import content.minigame.barbassault.getBALevels
import core.api.sendMessage
import core.api.sendDialogueLines
import core.game.container.Container
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.item.Item
import org.rs09.consts.Items
fun getCollectorBag(player: Player): CollectorBagManager {
return player.getAttribute<CollectorBagManager>(CollectorBagManager.KEY) ?: CollectorBagManager(player).also { player.setAttribute(CollectorBagManager.KEY, it) }
}
fun removeCollectorBag(player: Player) = player.removeAttribute(CollectorBagManager.KEY)
fun resetCollectorBag(player: Player) = player.setAttribute(CollectorBagManager.KEY,CollectorBagManager(player))
class CollectorBagManager(val player: Player) {
val bag: CollectorBag = CollectorBag(getCollectorBagCapacity(getBALevels(player).col))
fun getCollectorBagCapacity(level: Int): Int = when (level) { 1 -> 2; 2 -> 3; 3 -> 6; 4 -> 7; 5 -> 8; else -> 2 }
fun lookInBag() {
val green = bag.getEggCount(Items.GREEN_EGG_10531)
val red = bag.getEggCount(Items.RED_EGG_10532)
val blue = bag.getEggCount(Items.BLUE_EGG_10533)
sendDialogueLines(player,"The collection bag contains:","$green poison eggs, $red explosive eggs","and $blue stun eggs.")
}
companion object {
val VALID_EGGS = setOf(Items.RED_EGG_10532,Items.GREEN_EGG_10531,Items.BLUE_EGG_10533)
val KEY = "collector_bag_manager"
}
class CollectorBag(val capacity: Int) {
var container = Container(3)
fun totalEggs(): Int = container.toArray().filterNotNull().sumOf { it.amount }
fun freeEggSpace(): Int = capacity - totalEggs()
fun getEggCount(itemId: Int): Int = container.toArray().filterNotNull().filter { it.id == itemId }.sumOf { it.amount }
}
fun addToBag(node: Node): Boolean {
if (node.id !in VALID_EGGS) return false
if (bag.freeEggSpace() <= 0) { sendMessage(player, "Your bag is full."); return false }
val egg = Item(node.id, 1)
bag.container.add(egg)
sendMessage(player, "You put the egg in the bag.")
return true
}
}

View file

@ -0,0 +1,95 @@
package content.minigame.barbassault.arena
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.getBASession
import core.api.setComponentVisibility
import core.api.setInterfaceText
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Components
// Todo find actual varbits for controlling the interface
@Initializable
class HealerInterface : ComponentPlugin() {
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(HealerIface.ID).plugin = this
return this
}
override fun open(player: Player?, component: Component?) {
super.open(player, component)
player ?: return
setHealerTeamIfaceSlot(player,0)
setHealerTeamIfaceSlot(player,1)
setHealerTeamIfaceSlot(player,2)
setHealerTeamIfaceSlot(player,3)
}
override fun handle( player: Player?, component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int): Boolean {
return true
}
}
fun setHealerTeamIfaceSlot(player: Player,slot: Int){
val slotIface = HealerIface.team[slot]
val slotPlayer = getPlayerHealerIfaceSlot(player,slot)
val slotHealth = HealerIface.formatHp(slotPlayer?.skills?.lifepoints ,slotPlayer?.skills?.maximumLifepoints)
//todo find a way to crop the model.
//20578 is the correct model
//setInterfaceModel(player, 20578, HealerIface.ID, slotIface.roleChild, HealerIface.modelZoom,572,0) // role Icon
setInterfaceText(player, slotPlayer?.username.toString(), HealerIface.ID, slotIface.nameChild) //Name
setInterfaceText(player, slotHealth, HealerIface.ID, slotIface.hpChild)// Health
}
fun getPlayerHealerIfaceSlot(player:Player,slot: Int): Player? {
val team = getBASession(player)?.team
val others = team?.allPlayers()?.filter { it != player }
return others?.getOrNull(slot)
}
fun HealerIfaceMedicHelp(player: Player, other: Player, toggle: Boolean = false) {
if (player == other) return
val team = getBASession(player)?.team ?: return
val mySlot = team.slotOf(player)
val otherSlot = team.slotOf(other)
val uiIndex = if (otherSlot > mySlot) otherSlot - 1 else otherSlot
if (uiIndex !in HealerIface.team.indices) return
val slotIface = HealerIface.team[uiIndex]
setComponentVisibility(player, HealerIface.ID, slotIface.helpChild, toggle)
}
fun HealerIfaceHPUpdatePlayer(player:Player, other:Player ){
if (player == other) return
val team = getBASession(player)?.team ?: return
val mySlot = team.slotOf(player)
val otherSlot = team.slotOf(other)
val uiIndex = if (otherSlot > mySlot) otherSlot - 1 else otherSlot
if (uiIndex !in HealerIface.team.indices) return
setHealerTeamIfaceSlot(player,uiIndex)
}
object HealerIface {
const val ID = Components.BARBASSAULT_OVER_HEAL_488
val modelZoom = 2372//1940
//these are not the correct models for this Iface, but we could use them
val ROLE_MODEL_MAP = mapOf(BarbRole.ATTACKER to 20561, BarbRole.COLLECTOR to 20563, BarbRole.DEFENDER to 20566, BarbRole.HEALER to 20569)
data class Slot(val roleChild: Int, val nameChild: Int, val hpChild:Int, val helpChild: Int)
val team = listOf(
Slot(16, 3, 4, 5),
Slot(17, 9, 10, 8),
Slot(18, 14, 15, 13),
Slot(19, 19, 20, 18))
fun formatHp(current: Int?, max: Int?) = "HP $current/$max"
}

View file

@ -0,0 +1,36 @@
package content.minigame.barbassault.arena
class WaveCompleteInterface {
}
/*
[TITLE] bold/ Wave Complete!
bold/ Reward:
Bold/ The Queen is dead!
Reward:
80 Attacker Points
5 Defender Points
5 Collector Points
5 Healer Points
The option to send your points
for
special armour and gambling
options,
which you should use before
defeating the queen again.
advance break down -->
https://youtu.be/OjkLXOQVWEQ?t=113
https://www.youtube.com/watch?v=gpSmUZcaMwY
//error message if button level up, "Sorry, you don't have enough points to level up
*/

View file

@ -0,0 +1,125 @@
package content.minigame.barbassault.arena
import org.rs09.consts.NPCs
fun getWaveDefinition(index: Int): WaveDefinition {
return when (index) {
10 -> waves[7]
else -> waves[index]
}
}
data class WaveDefinition( val caps: SpawnCaps, val npcIds: Map<PenanceType, Int> )
enum class PenanceType { HEALER, RUNNER, FIGHTER, RANGER, QUEEN }
data class SpawnCaps(var rangers: Int, var fighters: Int, var runners: Int, var healers: Int) {
operator fun get(type: PenanceType): Int =
when (type) {
PenanceType.RANGER -> rangers
PenanceType.FIGHTER -> fighters
PenanceType.RUNNER -> runners
PenanceType.HEALER -> healers
else -> {0}
}
operator fun set(type: PenanceType, value: Int) {
when (type) {
PenanceType.RANGER -> rangers = value
PenanceType.FIGHTER -> fighters = value
PenanceType.RUNNER -> runners = value
PenanceType.HEALER -> healers = value
else -> {0}
}
}
}
private val waves = listOf(
WaveDefinition( //Wave tut
SpawnCaps(rangers = 1, fighters = 1, runners = 1, healers = 1),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5043,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5042,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5040,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5041
)
),
WaveDefinition( //Wave 1
SpawnCaps(rangers = 4, fighters = 4, runners = 2, healers = 2),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5238,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5220,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5044,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5229 // missing death animation
)
),
WaveDefinition( //Wave 2
SpawnCaps(rangers = 4, fighters = 5, runners = 3, healers = 3),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5239,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5221,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5045,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5230
)
),
WaveDefinition( //Wave 3
SpawnCaps(rangers = 6, fighters = 5, runners = 4, healers = 3),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5240,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5222,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5213,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5231
)
),
WaveDefinition( //Wave 4
SpawnCaps(rangers = 6, fighters = 6, runners = 4, healers = 4),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5241,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5223,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5214,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5232
)
),
WaveDefinition( //Wave 5
SpawnCaps(rangers = 6, fighters = 6, runners = 5, healers = 5),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5242,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5224,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5215,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5233
)
),
WaveDefinition( //Wave 6
SpawnCaps(rangers = 7, fighters = 6, runners = 6, healers = 6),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5243,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5225,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5216,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5234
)
),
WaveDefinition( //Wave 7 //Also used in wave 10
SpawnCaps(rangers = 7, fighters = 7, runners = 6, healers = 7),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5244,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5226,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5217,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5235
)
),
WaveDefinition( //Wave 8
SpawnCaps(rangers = 8, fighters = 7, runners = 7, healers = 7),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5245,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5227,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5218,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5236
)
),
WaveDefinition( //Wave 9
SpawnCaps(rangers = 8, fighters = 8, runners = 9, healers = 8),
mapOf(
PenanceType.HEALER to NPCs.PENANCE_HEALER_5246,
PenanceType.RUNNER to NPCs.PENANCE_RUNNER_5228,
PenanceType.FIGHTER to NPCs.PENANCE_FIGHTER_5219,
PenanceType.RANGER to NPCs.PENANCE_RANGER_5237
)
),
)

View file

@ -0,0 +1,38 @@
package content.minigame.barbassault.arena.npcs
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs
class EggCannonNPC : NPCBehavior(NPCs.EGG_LAUNCHER_5026) {
override fun onCreation (self: NPC) {
self.isWalks = false
self.isNeverWalks = true
self.walkRadius = 0
self.dropLocation
self.definition.combatGraphics[1] = Graphics(866, 25, 0)
//5426 = attackanimation
self.properties.rangeAnimation = Animation.create(5426)
self.definition.combatDistance = 14
self.properties.combatPulse.style = CombatStyle.RANGE
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
override fun tick(self: NPC): Boolean {
return true
}
fun fireEggAt(target:String,eggColor: String){
// RegionManager.getSurroundingNPCs()
}
}

View file

@ -0,0 +1,110 @@
package content.minigame.barbassault.arenas.monsters
import content.minigame.barbassault.arena.BACombatValidation
import content.minigame.barbassault.arena.BarbAssEvent
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.arena.npcs.dropPenanceEggCluster
import content.minigame.barbassault.getBASession
import core.api.EquipmentSlot
import core.api.getItemFromEquipment
import core.api.sendMessage
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatStyle
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.update.flag.context.Animation
import org.rs09.consts.NPCs
val PENANCE_FIGHTER_IDS = listOf(
//NPCs.PENANCE_FIGHTER_5040, //tutorial
NPCs.PENANCE_FIGHTER_5044,NPCs.PENANCE_FIGHTER_5045,NPCs.PENANCE_FIGHTER_5213,
NPCs.PENANCE_FIGHTER_5214,NPCs.PENANCE_FIGHTER_5215,NPCs.PENANCE_FIGHTER_5216,
NPCs.PENANCE_FIGHTER_5217,NPCs.PENANCE_FIGHTER_5218,NPCs.PENANCE_FIGHTER_5219)
data class PenanceFighterDefinition(val id: Int,val hp: Int,val attack: Int,val strength: Int, val defence: Int, val range: Int, val magic: Int)
val PENANCE_FIGHTER_DEFINITIONS = listOf(
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5044, 28, 26, 26, 25, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5045, 29, 28, 29, 27, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5213, 32, 33, 32, 34, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5214, 37, 36, 38, 37, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5215, 38, 41, 43, 44, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5216, 49, 50, 48, 48, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5217, 50, 56, 55, 52, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5218, 55, 60, 61, 62, 1, 1),
PenanceFighterDefinition(NPCs.PENANCE_FIGHTER_5219, 56, 71, 73, 66, 1, 1),
)
val PENANCE_FIGHTER_DEFINITIONS_BY_ID = PENANCE_FIGHTER_DEFINITIONS.associateBy { it.id }
class PenanceFighterNPC : NPCBehavior(*PENANCE_FIGHTER_IDS.toIntArray()) {
override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean {
if (attacker is Player && getItemFromEquipment(attacker, EquipmentSlot.CAPE)?.id != 10556) {
//todo make list of not allowed weapons to attack IE: Crystal bow, Karils Crossbow
sendMessage(attacker,"These Penance are immune to your attacks.")
return false
}
return true
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
val player = attacker as? Player ?: return
//if (!player.isArtificial) {
state.estimatedHit = BACombatValidation.applyAttackValidation(self, player, state)
//}
}
override fun tick(self: NPC): Boolean {
self.shouldPreventStacking(self)
return super.tick(self)
}
override fun onCreation (self: NPC) {
self.setAttribute("agg_radius",6)
self.isWalks = true
self.properties.isNPCWalkable = false
self.walkRadius = 24
self.isRespawn = false
self.setAttribute("barbass-type", PenanceType.FIGHTER)
self.definition.combatDistance = 1
self.properties.combatPulse.style = CombatStyle.MELEE
self.isAggressive = true
self.properties.defenceAnimation = Animation(5096)
self.properties.attackAnimation = Animation(5097)
self.properties.deathAnimation = Animation(5098)
self.properties.attackSpeed = 4
val definition = PENANCE_FIGHTER_DEFINITIONS_BY_ID[self.id] ?: return
self.getSkills().setStaticLevel(Skills.ATTACK, definition.attack)
self.getSkills().setStaticLevel(Skills.STRENGTH, definition.strength)
self.getSkills().setStaticLevel(Skills.DEFENCE, definition.defence)
self.getSkills().setStaticLevel(Skills.RANGE, definition.range)
self.getSkills().setStaticLevel(Skills.MAGIC, definition.magic)
self.getSkills().setStaticLevel(Skills.HITPOINTS, definition.hp)
}
override fun onDeathFinished(self: NPC, killer: Entity) {
val player = killer as? Player ?: return
val session = getBASession(self) ?: return
dropPenanceEggCluster(killer,self)
session.eventBus.emit(BarbAssEvent.NPCDied(self,player))
killer.removeAttribute("combat-time")
super.onDeathFinished(self, killer)
}
}
//Shrink death 5098
//Fighter_Attack_5097
//5096 take hit
//5095_Move
//9094_Idle

View file

@ -0,0 +1,263 @@
package content.minigame.barbassault.arena.npcs
import content.minigame.barbassault.BA_SESSION_KEY
import content.minigame.barbassault.applyBAPoison
import content.minigame.barbassault.isBAPoisoned
import content.minigame.barbassault.arena.BarbAssEvent
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.PenanceType
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
NPCs.PENANCE_HEALER_5238,NPCs.PENANCE_HEALER_5239,NPCs.PENANCE_HEALER_5240,
NPCs.PENANCE_HEALER_5241,NPCs.PENANCE_HEALER_5242,NPCs.PENANCE_HEALER_5243,
NPCs.PENANCE_HEALER_5244,NPCs.PENANCE_HEALER_5245,NPCs.PENANCE_HEALER_5246
)
data class PenanceHealerDefinition(val id: Int,val hp: Int,val attack: Int,val strength: Int, val defence: Int, val range: Int, val magic: Int)
val PENANCE_HEALER_DEFINITIONS = listOf(
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5238, 27, 9, 8, 9, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5239, 32, 13, 15, 14, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5240, 37, 20, 18, 19, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5241, 43, 22, 23, 21, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5242, 49, 26, 27, 25, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5243, 55, 30, 32, 28, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5244, 60, 33, 36, 34, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5245, 67, 40, 38, 36, 1, 1 ),
PenanceHealerDefinition(NPCs.PENANCE_HEALER_5246, 76, 40, 44, 38, 1, 1 ),
)
val PENANCE_HEALER_DEFINITIONS_BY_ID = PENANCE_HEALER_DEFINITIONS.associateBy { it.id }
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.setAttribute(MODE, HealerMode.TARGET_PLAYER)
self.isWalks = false
self.walkRadius = 0
self.isRespawn = false
self.properties.defenceAnimation = Animation(5105)
self.properties.deathAnimation = Animation(5106)
self.properties.attackAnimation = Animation(5107)
val definition = PENANCE_HEALER_DEFINITIONS_BY_ID[self.id] ?: return
self.getSkills().setLevel(Skills.ATTACK, definition.attack)
self.getSkills().setLevel(Skills.STRENGTH, definition.strength)
self.getSkills().setLevel(Skills.DEFENCE, definition.defence)
self.getSkills().setLevel(Skills.RANGE, definition.range)
self.getSkills().setLevel(Skills.MAGIC, definition.magic)
self.getSkills().setLevel(Skills.HITPOINTS, definition.hp)
self.getSkills().setStaticLevel(Skills.HITPOINTS, definition.hp)
}
override fun tick(self: NPC): Boolean {
val session = self.getAttribute<BarbassaultSession>(BA_SESSION_KEY) ?: return false
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)
wander(self)
return
}
if (!isCloseEnough(self, target)) {
forceWalk(self, target.location, "DUMB")
return
}
self.resetWalk()
self.face(target)
if (!isBAPoisoned(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, 1)
}
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)
wander(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)
val player = killer as? Player ?: return
val session = getBASession(player) ?: return
dropPenanceEggCluster(killer,self)
session.eventBus.emit(BarbAssEvent.NPCDied(self,player))
}
}
/*
Can only heal Penance Runners
Can only be Poisoned by Healer roll
Poisoned tofu/Poisoned worms/Poisoned meat announced by Defender
poisoned food will cause a total of 54 damage over time.
First spawn = will target visible player then target visible Penance Runner,heal it to full health. repeat
No runners = Randomly Roam
Poisoned tofu = 10539
Poisoned worms = 10540
Poisoned meat = 10541
Healer emote:
healer_Idle_5104
healer_hit_5105
healer_Death_5106
Healer_attack_5107?
*/

View file

@ -0,0 +1,113 @@
package content.minigame.barbassault.arena.npcs
import content.minigame.barbassault.arena.BAGroundSpawn
import content.minigame.barbassault.getBASession
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.RegionManager
import org.rs09.consts.Items
/**
* Drops a cluster of Penance eggs around a target location in Barbarian Assault.
*
* Eggs are distributed randomly across a 3x3 area centered on the NPC drop location,
* or the killer's location if no NPC is provided.
*
* @param killer The player who triggered the egg drop (must have a BA session).
* @param npc Optional NPC used as the center point for the drop location.
*/
fun dropPenanceEggCluster(killer: Player, npc: NPC? = null) {
val center = npc?.dropLocation ?: killer.location
val tiles = center.get3x3Tiles().shuffled()
val session = getBASession(killer) ?: return
val eggs = listOf( Items.RED_EGG_10532, Items.RED_EGG_10532,
Items.BLUE_EGG_10533, Items.BLUE_EGG_10533,
Items.GREEN_EGG_10531, Items.GREEN_EGG_10531)
for (i in eggs.indices) {
val tile = tiles[i]
val thisGroundItem = BAGroundSpawn(session,60,Item(eggs[i],1),tile)
thisGroundItem.init()
}
}
//GroundItemManager.create(Item(eggs[i], 1), tile).apply { forceVisible = true }
// GroundItemManager.create(Item(Items.RED_EGG_10532, 1), tile).apply { forceVisible = true }
// GroundItemManager.create(Item(eggs[i], 1), tile, null)
/**
* Finds the closest visible player to the given NPC within a specified range.
*
* @param npc The NPC used as the search origin.
* @param range Maximum distance to search for players.
* @return The closest valid Player, or null if none are found.
*/
fun findClosestPlayerFromNPC(npc: NPC,range:Int = 14): Player? {
var closest: Player? = null
var bestDistance = Double.MAX_VALUE
for (player in RegionManager.getLocalPlayers(npc, range)) {
if (player.isInvisible) continue
val dist = npc.location.getDistance(player.location)
if (dist < bestDistance) {
bestDistance = dist
closest = player
}
}
return closest
}
/**
* Finds the closest NPC matching the given target NPC within a range,
*
* @param targetNPC The specific NPC being searched for.
* @param npc The source NPC performing the search.
* @param range Maximum search radius.
* @return The closest matching NPC if found and visible, otherwise null.
*/
fun findClosestTargetNPCIDFromNPC(targetNPC: Int?, npc: NPC, range: Int = 14): NPC? {
var closest: NPC? = null
var bestDistance = Double.MAX_VALUE
for (foundNpc in RegionManager.getLocalNpcs(npc, range)) {
if (targetNPC != foundNpc.id) continue
val dist = npc.location.getDistance(foundNpc.location)
if (dist < bestDistance) {
bestDistance = dist
closest = foundNpc
}
}
return closest
}
/**
* Finds the closest NPC to the given player within a specified range
* that matches the provided NPC ID.
*
* @param player The player used as the search origin.
* @param npcId The ID of the NPC to search for.
* @param range Maximum distance to search for NPCs.
* @return The closest matching NPC, or null if none are found.
*/
fun findClosestNPCIDFromPlayer(player: Player, targetNPC: Int, range: Int = 14): NPC? {
var closest: NPC? = null
var bestDistance = Double.MAX_VALUE
for (npc in RegionManager.getLocalNpcs(player, range)) {
if (npc.id != targetNPC) continue
val dist = player.location.getDistance(npc.location)
if (dist < bestDistance) {
bestDistance = dist
closest = npc
}
}
return closest
}

View file

@ -0,0 +1,36 @@
package content.minigame.barbassault.arena.npcs
import core.api.sendMessage
import core.game.node.entity.Entity
import core.game.node.entity.combat.CombatStyle
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import core.game.node.entity.player.Player
import org.rs09.consts.NPCs
//.configureBossData()
class PenanceQueenNPC : NPCBehavior(NPCs.PENANCE_QUEEN_5247){
override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean {
if (attacker is Player ) {
sendMessage(attacker,"Only an Omega Egg can hurt the queen!")
return false
}
return true
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
}
/*
queen emote:
queen_hit_5408+
queen_move_5409
queen_Idle_5410
queen_attack_5411
queen_Death_5412
queen_attack2_5413
queen_Spawnin_5414
*/

View file

@ -0,0 +1,114 @@
package content.minigame.barbassault.arena.npcs
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbAssEvent
import content.minigame.barbassault.arena.BACombatValidation
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.getBASession
import core.api.*
import core.api.getItemFromEquipment
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatStyle
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.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs
//20730 = rangers cast
val PENANCE_RANGER_IDS = listOf(
// NPCs.PENANCE_RANGER_5041, // tut
NPCs.PENANCE_RANGER_5229,NPCs.PENANCE_RANGER_5230,NPCs.PENANCE_RANGER_5231,
NPCs.PENANCE_RANGER_5232,NPCs.PENANCE_RANGER_5233,NPCs.PENANCE_RANGER_5234,
NPCs.PENANCE_RANGER_5235,NPCs.PENANCE_RANGER_5236,NPCs.PENANCE_RANGER_5237
)
data class PenanceRangerDefinition(val id: Int,val hp: Int,val attack: Int,val strength: Int, val defence: Int, val range: Int, val magic: Int)
val PENANCE_RANGER_DEFINITIONS = listOf(
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5229, 20, 22, 1, 21, 23, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5230, 28, 27, 1, 29, 24, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5231, 29, 32, 1, 33, 34, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5232, 34, 41, 1, 42, 40, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5233, 41, 44, 1, 46, 45, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5234, 50, 51, 1, 54, 52, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5235, 50, 63, 1, 61, 62, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5236, 54, 69, 1, 68, 70, 1),
PenanceRangerDefinition(NPCs.PENANCE_RANGER_5237, 58, 79, 1, 80, 78, 1),
)
val PENANCE_RANGER_DEFINITIONS_BY_ID = PENANCE_RANGER_DEFINITIONS.associateBy { it.id }
class PenanceRangerNPC : NPCBehavior(*PENANCE_RANGER_IDS.toIntArray()) {
override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean {
if (attacker is Player && getItemFromEquipment(attacker, EquipmentSlot.CAPE)?.id != 10556) {
//todo make list of not allowed weapons to attack IE: Crystal bow, Karils Crossbow
sendMessage(attacker, "These Penance are immune to your attacks.")
return false
}
return true
}
override fun beforeDamageReceived( self: NPC, attacker: Entity, state: BattleState) {
attacker as Player
// if(!attacker.isArtificial) {
state.estimatedHit = BACombatValidation.applyAttackValidation( self, attacker, state )
// }
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
override fun onCreation (self: NPC) {
self.setAttribute("agg_radius",6)
self.isWalks = true
self.isNeverWalks = false
self.walkRadius = 24
self.isRespawn = false
self.definition.combatGraphics[1] = Graphics(866, 25, 0)
self.setAttribute("barbass-type", PenanceType.RANGER)
self.definition.combatDistance = 8
self.properties.combatPulse.style = CombatStyle.RANGE
self.isAggressive = true
self.properties.defenceAnimation = Animation(5396)
self.properties.attackAnimation = Animation(5395)
self.properties.rangeAnimation = Animation(5395)
self.properties.deathAnimation = Animation(5397)
self.properties.attackSpeed = 4
val definition = PENANCE_RANGER_DEFINITIONS_BY_ID[self.id] ?: return
self.getSkills().setStaticLevel(Skills.ATTACK, definition.attack)
self.getSkills().setStaticLevel(Skills.STRENGTH, definition.strength)
self.getSkills().setStaticLevel(Skills.DEFENCE, definition.defence)
self.getSkills().setStaticLevel(Skills.RANGE, definition.range)
self.getSkills().setStaticLevel(Skills.MAGIC, definition.magic)
self.getSkills().setStaticLevel(Skills.HITPOINTS, definition.hp)
}
override fun onDeathFinished(self: NPC, killer: Entity) {
super.onDeathFinished(self, killer)
val player = killer as? Player ?: return
val session = getBASession(killer) ?: return
killer.removeAttribute("combat-time")
dropPenanceEggCluster(killer,self)
session.eventBus.emit(BarbAssEvent.NPCDied(self,player))
}
}
/*
* Combat-style/arrow-type/magic-spell announced by Collector
* Controlled/Bullet/Wind
* Accurate/Field/Water
* Aggressive/Blunt/Earth
* Defensive/Barbed/Fire
* Poisons (from both weapons and green eggs)
* Egg fired from the Egg Launcher
* Recoil damage from any player
*/

View file

@ -0,0 +1,374 @@
package content.minigame.barbassault.arena.npcs
import content.minigame.barbassault.BA_SESSION_KEY
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.LureItems
import content.minigame.barbassault.getBALevels
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import org.rs09.consts.NPCs
import content.minigame.barbassault.arena.*
import content.minigame.barbassault.arena.scenery.DefenderTrap
import content.minigame.barbassault.getBASession
import core.api.forceWalk
import core.api.hasLineOfSight
import core.api.sendChat
import core.game.node.item.GroundItemManager
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 kotlin.math.abs
import kotlin.math.max
val PENANCE_RUNNER_IDS = listOf(
//NPCs.PENANCE_RUNNER_5042,//tut
NPCs.PENANCE_RUNNER_5220,
NPCs.PENANCE_RUNNER_5221,
NPCs.PENANCE_RUNNER_5222,
NPCs.PENANCE_RUNNER_5223,
NPCs.PENANCE_RUNNER_5224,
NPCs.PENANCE_RUNNER_5225,
NPCs.PENANCE_RUNNER_5226,
NPCs.PENANCE_RUNNER_5227,
NPCs.PENANCE_RUNNER_5228,
)
class PenanceRunnerNPC : NPCBehavior(*PENANCE_RUNNER_IDS.toIntArray()) {
val GOOD_EAT = "Chomp, chomp"
val BAD_EAT = "Blurghh"
val TRAP_HIT = "Urghhh!"
val ESCAPE = "Raaa!!"
private val defenderTrap = DefenderTrap()
private enum class RunnerMode { RANDOM_WALKING, HUNTING }
private companion object {
const val MODE = "ba-runner:mode"
const val MODE_TICKS = "ba-runner:mode-ticks"
const val HUNT_TICKS = "ba-runner:hunt-ticks"
const val TARGET = "ba-runner:target"
const val TARGET_CYCLES = "ba-runner:target-cycles"
const val TARGET_LAST_LOC = "ba-runner:target-last-loc"
const val SKIP_NEXT_HUNT = "ba-runner:skip-next-hunt"
const val BAD_FOOD_WALK_TICKS = "ba-runner:bad-food-walk-ticks"
const val EAT_PAUSE_TICKS = "ba-runner:eat-pause-ticks"
const val ESCAPE_REMOVE_TICKS = "ba-runner:escape-remove-ticks"
val HUNT_ZONE_ORDER = (0 downTo -5).flatMap { x ->
(0 downTo -5).map { y -> x to y }
}
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
override fun onCreation(self: NPC) {
self.isRespawn = false
self.isWalks = false
self.walkRadius = 0
self.destinationFlag
self.setAttribute("barbass-type", PenanceType.RUNNER)
self.setAttribute(MODE, RunnerMode.RANDOM_WALKING)
self.setAttribute(MODE_TICKS, (0..4).random())
self.setAttribute(HUNT_TICKS, (0..2).random())
self.properties.defenceAnimation = Animation(5101)
self.properties.deathAnimation = Animation(5106)
self.getSkills().setStaticLevel(Skills.HITPOINTS, 5)
}
override fun tick(self: NPC): Boolean {
val session = self.getAttribute<BarbassaultSession>(BA_SESSION_KEY) ?: return false
if (defenderTrap.tryTriggerTrap(self)) {
return false
}
val escapeRemoveTicks = self.getAttribute(ESCAPE_REMOVE_TICKS, 0)
if (escapeRemoveTicks > 0) {
self.setAttribute(ESCAPE_REMOVE_TICKS, escapeRemoveTicks - 1)
self.resetWalk()
if (escapeRemoveTicks <= 1) {
session.runnerEscaped(self)
resetAfterEscape(self)
}
return false
}
if (isAtEscapeTunnel(session, self.location)) {
beginEscape(self)
return false
}
val eatPauseTicks = self.getAttribute(EAT_PAUSE_TICKS, 0)
if (eatPauseTicks > 0) {
self.setAttribute(EAT_PAUSE_TICKS, eatPauseTicks - 1)
self.resetWalk()
self.shouldPreventStacking(self)
return false
}
val modeChanged = tickModeTimer(self)
tickHuntTimer(self, session)
val badWalkTicks = self.getAttribute(BAD_FOOD_WALK_TICKS, 0)
if (badWalkTicks > 0) {
self.setAttribute(BAD_FOOD_WALK_TICKS, badWalkTicks - 1)
walkAfterBadFood(self, session)
self.shouldPreventStacking(self)
return false
}
val target = self.getAttribute<BAGroundSpawn?>(TARGET, null)
if (target != null) {
chaseFood(self, session, target)
} else if (modeChanged && self.getAttribute(MODE, RunnerMode.RANDOM_WALKING) == RunnerMode.RANDOM_WALKING) {
randomWalk(self, session)
}
self.shouldPreventStacking(self)
return false
}
private fun tickModeTimer(self: NPC): Boolean {
val modeTicks = self.getAttribute(MODE_TICKS, 0) + 1
if (modeTicks < 5) {
self.setAttribute(MODE_TICKS, modeTicks)
return false
}
self.setAttribute(MODE_TICKS, 0)
val nextMode = when (self.getAttribute(MODE, RunnerMode.RANDOM_WALKING)) {
RunnerMode.RANDOM_WALKING -> RunnerMode.HUNTING
RunnerMode.HUNTING -> RunnerMode.RANDOM_WALKING
}
self.setAttribute(MODE, nextMode)
if (nextMode == RunnerMode.HUNTING) {
val targetCycles = self.getAttribute(TARGET_CYCLES, 0) + 1
self.setAttribute(TARGET_CYCLES, targetCycles)
}
return true
}
private fun tickHuntTimer(self: NPC, session: BarbassaultSession) {
if (self.getAttribute(MODE, RunnerMode.RANDOM_WALKING) != RunnerMode.HUNTING) {
return
}
val huntTicks = self.getAttribute(HUNT_TICKS, 0) + 1
if (huntTicks < 3) {
self.setAttribute(HUNT_TICKS, huntTicks)
return
}
self.setAttribute(HUNT_TICKS, 0)
if (self.getAttribute(SKIP_NEXT_HUNT, false)) {
self.setAttribute(SKIP_NEXT_HUNT, false)
clearTarget(self)
return
}
val currentTarget = self.getAttribute<BAGroundSpawn?>(TARGET, null)
if (currentTarget != null && self.getAttribute(TARGET_CYCLES, 0) < 2 && isFoodStillActive(session, currentTarget)) {
return
}
selectFood(self, session)?.let { food ->
self.setAttribute(TARGET, food)
self.setAttribute(TARGET_CYCLES, 0)
self.setAttribute(TARGET_LAST_LOC, food.location)
}
}
private fun chaseFood(self: NPC, session: BarbassaultSession, food: BAGroundSpawn) {
val foodLocation = food.location
if (!isFoodStillActive(session, food)) {
clearTarget(self)
forceWalk(self, foodLocation, "DUMB")
return
}
if (self.location == foodLocation) {
eatFood(self, session, food)
return
}
val occupied = RegionManager.getLocalEntitys(foodLocation, 0).any { it != self && it.location == foodLocation }
if (occupied) {
if (!foodLocation.isNextTo(self)) {
val adjacent = foodLocation.cardinalTiles
.filter { RegionManager.isTeleportPermitted(it) }
.minByOrNull { it.getDistance(self.location) }
if (adjacent != null) {
forceWalk(self, adjacent, "DUMB")
}
} else {
self.resetWalk()
}
self.faceLocation(foodLocation)
return
}
forceWalk(self, foodLocation, "DUMB")
}
private fun eatFood(self: NPC, session: BarbassaultSession, food: BAGroundSpawn) {
val goodFood = food.flag == true
sendChat(self, if (goodFood) GOOD_EAT else BAD_EAT)
session.destroyGroundItem(food)
clearTarget(self)
self.resetWalk()
if (goodFood) {
self.setAttribute(EAT_PAUSE_TICKS, 5)
if (self.getAttribute(MODE, RunnerMode.RANDOM_WALKING) == RunnerMode.HUNTING) {
self.setAttribute(MODE, RunnerMode.RANDOM_WALKING)
self.setAttribute(MODE_TICKS, 0)
}
return
}
self.setAttribute(SKIP_NEXT_HUNT, true)
self.setAttribute(BAD_FOOD_WALK_TICKS, 5)
walkAfterBadFood(self, session)
}
private fun selectFood(self: NPC, session: BarbassaultSession): BAGroundSpawn? {
val radius = lureRadius(session)
val allVisibleFood = session.BAgroundItems
.filter { isFoodStillActive(session, it) }
.filter { it.id in LureItems }
.filter { hasLineOfSight(self, it) }
if (allVisibleFood.none { tileRadius(self.location, it.location) <= radius }) {
return null
}
for (zone in HUNT_ZONE_ORDER) {
val food = allVisibleFood.lastOrNull { zoneFor(session, it.location) == zone }
if (food != null) {
return food
}
}
return null
}
private fun randomWalk(self: NPC, session: BarbassaultSession) {
val location = self.location
val destination = if (isAtSouthernWall(session, location)) {
furthestWalkableStraight(location, 0, -1, 5) ?: escapeLocation(session)
} else {
when ((0..5).random()) {
0 -> furthestWalkableStraight(location, -1, 0, 5)
1 -> furthestWalkableStraight(location, 1, 0, 5)
else -> furthestWalkableStraight(location, 0, -1, 5)
}
} ?: return
forceWalk(self, destination, "DUMB")
}
private fun walkAfterBadFood(self: NPC, session: BarbassaultSession) {
val directionY = if (self.location.y >= session.base.y + 42) -1 else 1
val destination = furthestWalkableStraight(self.location, 0, directionY, 5) ?: return
forceWalk(self, destination, "DUMB")
}
private fun lureRadius(session: BarbassaultSession): Int {
return if (session.team.getPlayersByRole(BarbRole.DEFENDER).any { getBALevels(it).def >= 2 }) 5 else 4
}
private fun isFoodStillActive(session: BarbassaultSession, food: BAGroundSpawn): Boolean {
return food.isActive && !food.isRemoved && session.BAgroundItems.contains(food) && GroundItemManager.getItems().contains(food)
}
private fun clearTarget(self: NPC) {
self.removeAttribute(TARGET)
self.removeAttribute(TARGET_CYCLES)
self.removeAttribute(TARGET_LAST_LOC)
}
private fun zoneFor(session: BarbassaultSession, location: Location): Pair<Int, Int> {
val relativeX = location.x - session.base.x
val relativeY = location.y - session.base.y
return Math.floorDiv(relativeX - 40, 8) to Math.floorDiv(relativeY - 40, 8)
}
private fun tileRadius(a: Location, b: Location): Int {
return max(abs(a.x - b.x), abs(a.y - b.y))
}
private fun furthestWalkableStraight(start: Location, dx: Int, dy: Int, maxSteps: Int): Location? {
var destination: Location? = null
for (step in 1..maxSteps) {
val next = start.transform(dx * step, dy * step, 0)
if (!RegionManager.isTeleportPermitted(next)) {
break
}
destination = next
}
return destination
}
private fun isAtSouthernWall(session: BarbassaultSession, location: Location): Boolean {
return location.y <= session.base.y + 16
}
private fun isAtEscapeTunnel(session: BarbassaultSession, location: Location): Boolean {
val escape = escapeLocation(session)
return location.y <= escape.y || (location.y <= escape.y + 1 && abs(location.x - escape.x) <= 2)
}
private fun beginEscape(self: NPC) {
sendChat(self, ESCAPE)
clearTarget(self)
self.resetWalk()
self.setAttribute(ESCAPE_REMOVE_TICKS, 1)
}
private fun resetAfterEscape(self: NPC) {
sendChat(self, "")
clearTarget(self)
self.removeAttribute(SKIP_NEXT_HUNT)
self.removeAttribute(BAD_FOOD_WALK_TICKS)
self.removeAttribute(EAT_PAUSE_TICKS)
self.removeAttribute(ESCAPE_REMOVE_TICKS)
self.setAttribute(MODE, RunnerMode.RANDOM_WALKING)
self.setAttribute(MODE_TICKS, (0..4).random())
self.setAttribute(HUNT_TICKS, (0..2).random())
self.resetWalk()
}
private fun escapeLocation(session: BarbassaultSession): Location {
return session.base.transform(35, 15, 0)
}
override fun onDeathFinished(self: NPC, killer: Entity) {
super.onDeathFinished(self, killer)
val player = killer as? Player ?: return
val session = getBASession(player) ?: return
dropPenanceEggCluster(killer,self)
session.eventBus.emit(BarbAssEvent.NPCDied(self,player))
}
}
//good food dropped stays "good"
//bad food dropped stays bad
/*
Runner emote
Runner_death_5103
Runner_trap_Death_ 5102
runner_hit_5101
runner_Move_5100
runner_Idle 5099
*/

View file

@ -0,0 +1,12 @@
package content.minigame.barbassault.arena.npcs
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import org.rs09.consts.NPCs
class PenanceSpawnNPC : NPCBehavior(NPCs.QUEEN_SPAWN_5248) {
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
}

View file

@ -0,0 +1,164 @@
package content.minigame.barbassault.arena.scenery
import content.minigame.barbassault.LureItems
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BAGroundSpawn
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.npcs.PENANCE_RUNNER_IDS
import content.minigame.barbassault.getBASession
import core.api.*
import core.game.node.entity.Entity
import core.game.node.entity.combat.DeathTask
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import org.rs09.consts.Scenery
// RUNNER_TRAP2_20135
// RUNNER_TRAP1_20230
// BROKEN_TRAP0_20231
//Trap East Location.create(1901, 5474, 0)
//trap west Location.create(1871, 5473, 0)
//emote 5365 maybe
//emote 5416 long
//emote 5416 long
//emote 5417 block wall
//trap only kills if Runner eats GOOD bait.
//If runner eats bad bait they escape
class DefenderTrap : MapArea {
val GOOD_EAT = "Chomp, chomp"
val BAD_EAT = "Blurghh"
val TRAP_HIT = "Urghhh!"
val TRAP_EAST = Location.create(1901, 5474, 0)
val TRAP_WEST = Location.create(1871, 5473, 0)
val TRAP_EAST_ZONE: ZoneBorders = zoneFrom3x3(TRAP_EAST)
val TRAP_WEST_ZONE: ZoneBorders = zoneFrom3x3(TRAP_WEST)
val TRAPS_ZONE = arrayOf(TRAP_EAST_ZONE,TRAP_WEST_ZONE)
val Fix_trap_IDs = intArrayOf( Scenery.RUNNER_TRAP1_20230, Scenery.BROKEN_TRAP0_20231 )
val TRAPS = arrayOf(TRAP_WEST,TRAP_EAST)
val TRAP_EAST_OFFSET = Location.create(45, 34, 0)
val TRAP_WEST_OFFSET = Location.create(15, 33, 0)
val playersInArea = mutableListOf<Player>()
override fun defineAreaBorders(): Array<ZoneBorders> {
return TRAPS_ZONE
}
private fun zoneFrom3x3(center: Location): ZoneBorders {
val tiles = center.get3x3Tiles()
return ZoneBorders(
tiles.minOf { it.x },
tiles.minOf { it.y },
tiles.maxOf { it.x },
tiles.maxOf { it.y },
center.z
)
}
override fun areaEnter(entity: Entity) {
if (PENANCE_RUNNER_IDS.contains(entity.id)) {
tryTriggerTrap(entity)
}
val player = entity as? Player ?: return
playersInArea.add(player)
}
override fun areaLeave(entity: Entity, logout: Boolean) {
val player = entity as? Player ?: return
playersInArea.remove(player)
}
fun tryTriggerTrap(entity: Entity): Boolean {
if (DeathTask.isDead(entity)) {
return false
}
val session = getBASession(entity) ?: return false
val trapLocation = trapLocationFor(session, entity.location) ?: return false
val scenery = getScenery(trapLocation) ?: return false
if (scenery.id != Scenery.RUNNER_TRAP2_20135 && scenery.id != Scenery.RUNNER_TRAP1_20230) {
return false
}
val bait = goodTrapBait(session, trapLocation) ?: return false
session.destroyGroundItem(bait)
entity.sendChat(GOOD_EAT)
(entity as? NPC)?.let { session.runnerKilledByTrap(it) }
killRunner(entity)
updateTrap(scenery, trapLocation)
session.broadcastToRole(BarbRole.DEFENDER, "A Penance Runner broke a trap to the ${trapDirectionFor(session, trapLocation)}!")
sendMessageArea()
return true
}
fun killRunner(entity: Entity){
entity.sendChat(TRAP_HIT)
entity.startDeath(entity)
}
//step on to trap area, if good food "chomp chomp" stay in location
//set trap off
fun updateTrap(scenery: core.game.node.scenery.Scenery, trapLocation: Location) {
val nextTrap = when (scenery.id) {
Scenery.RUNNER_TRAP2_20135 -> Scenery.RUNNER_TRAP1_20230
Scenery.RUNNER_TRAP1_20230 -> Scenery.BROKEN_TRAP0_20231
else -> return
}
animateScenery(scenery, 5076)//5076
GameWorld.Pulser.submit(object : Pulse(3, scenery) {
override fun pulse(): Boolean {
replaceScenery(scenery, nextTrap, -1, trapLocation)
return true
}
})
}
private fun goodTrapBait(session: BarbassaultSession, trapLocation: Location): BAGroundSpawn? {
val trapTiles = trapLocation.get3x3Tiles()
return session.BAgroundItems.firstOrNull {
it.isActive &&
!it.isRemoved &&
it.id in LureItems &&
it.flag == true &&
trapTiles.contains(it.location)
}
}
private fun trapLocationFor(session: BarbassaultSession, location: Location): Location? {
return listOf(
session.base.transform(TRAP_EAST_OFFSET),
session.base.transform(TRAP_WEST_OFFSET)
).firstOrNull { trapLocation -> trapLocation.get3x3Tiles().contains(location) }
}
private fun trapDirectionFor(session: BarbassaultSession, trapLocation: Location): String {
return if (trapLocation == session.base.transform(TRAP_EAST_OFFSET)) "east" else "west"
}
fun sendMessageArea(){
playersInArea.forEach { player ->
sendMessage(player,"Entered Trap Area")
}
}
}
//player.baSession.broadcastToRole("A Penance Runner broke a trap to the east/west!")
//sendMessage(player,"A Penance Runner broke a trap to the east/west!") //When a runner dies to trap announce to Defenders
//replaceScenery()
//animateScenery
//5076

View file

@ -0,0 +1,344 @@
package content.minigame.barbassault.arena.scenery
import content.minigame.barbassault.BAGraphics.Blue_EGG_PROJECTILE_978
import content.minigame.barbassault.BAGraphics.GREEN_EGG_PROJECTILE_977
import content.minigame.barbassault.BAGraphics.RED_EGG_PROJECTILE_978
import content.minigame.barbassault.BAGraphics.YELLOW_EGG_PROJECTILE_980
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.npcs.findClosestNPCIDFromPlayer
import content.minigame.barbassault.arena.npcs.findClosestTargetNPCIDFromNPC
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.getBASession
import core.api.*
import core.game.component.CloseEvent
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import core.plugin.Plugin
import getCollectorBag
import org.rs09.consts.Items
import org.rs09.consts.NPCs
//Egg hoppper scenrey item updates based on eggs in hopper
// empty -> EGG_HOPPER_20264
// half -> EGG_HOPPER_20265
// full -> EGG_HOPPER_20266
//varbit 3268
@Initializable
class EggCannon : ComponentPlugin() {
//Easter Egg "Blinky Switch"
private val currentSequence = mutableListOf<Int>()
private val easterEggSequence = listOf(56, 38, 26, 26, 56, 56)
fun checkEasterEgg(player: Player,button: Int) {
currentSequence.add(button)
// println("Current sequence: $currentSequence + $button")
while(currentSequence.size > easterEggSequence.size) {
currentSequence.removeAt(0)
}
if(currentSequence.takeLast(easterEggSequence.size) == easterEggSequence) {
sendMessage(player, "[BlickySwitch] Activated!")
currentSequence.clear()
if (player.isAdmin) { //ADMIN ONLY!
val eggHopper = getBASession(player)?.eggHopper
eggHopper?.green = 99
eggHopper?.red = 99
eggHopper?.blue = 99
updateCannonUI(player)
}
}
}
//END BINKY//
data class EggHopper(var red: Int = 0, var blue: Int = 0, var green: Int = 0, var yellow: Int = 0 ){
private val maxPerColor = 5
val eggColorMap = mapOf(Items.GREEN_EGG_10531 to "green",Items.RED_EGG_10532 to "red",Items.BLUE_EGG_10533 to "blue",Items.OMEGA_EGG_10537 to "yellow" )
fun totalCount(): Int = red + blue + green + yellow
fun addEggs(redToAdd: Int = 0, blueToAdd: Int = 0, greenToAdd: Int = 0, yellowToAdd: Int = 0): Map<String, Int> {
val added = mutableMapOf<String, Int>()
val newRed = (red + redToAdd).coerceAtMost(maxPerColor)
added["red"] = newRed - red
red = newRed
val newBlue = (blue + blueToAdd).coerceAtMost(maxPerColor)
added["blue"] = newBlue - blue
blue = newBlue
val newGreen = (green + greenToAdd).coerceAtMost(maxPerColor)
added["green"] = newGreen - green
green = newGreen
val newYellow = (yellow + yellowToAdd).coerceAtMost(maxPerColor)
added["yellow"] = newYellow - yellow
yellow = newYellow
return added
}
fun addEgg(itemId: Int,count: Int = 0): Map<String, Int> {
return when (eggColorMap[itemId]) {
"red" -> addEggs(redToAdd = count)
"blue" -> addEggs(blueToAdd = count)
"green" -> addEggs(greenToAdd = count)
"yellow" -> addEggs(yellowToAdd = count)
else -> emptyMap()
}
}
fun consumeGreen(): Boolean { if (green <= 0) return false; green--; return true }
fun consumeRed(): Boolean { if (red <= 0) return false; red--; return true }
fun consumeBlue(): Boolean { if (blue <= 0) return false; blue--; return true }
fun consumeYellow(): Boolean { if (yellow <= 0) return false; yellow--; return true }
}
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(EggTurret.ID).plugin = this
ComponentDefinition.forId(EggTurret.ID).isWalkable = false;
return this
}
///stun(entity: Entity, ticks: Int)
// stunned monster cant take damage here?
///isStunned(entity: Entity)
//applyPoison (entity: Entity, source: Entity, severity: Int)
//isPoisoned (entity: Entity) : Boolean
//spawnProjectile
//face()
override fun open(player: Player?, component: Component?) {
super.open(player, component)
player ?: return
Component(EggTurret.ID).setCloseEvent(CloseEvent {p, _ -> closeTurretIface(p);return@CloseEvent true})
updateCannonUI(player)
}
//error message CHATBOX "[bold]all of the _PENANCE_ have been killed!"
//error messageif out of range CHATBOX "There are no targetable _PENANCE_ within fireing range."
override fun handle(player: Player?, component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int ): Boolean {
player ?: return true
checkEasterEgg(player, button)
if (button ==EggTurret.close ){ closeTurretIface(player); return true}
shootNormalEggs(button,player)
return true
}
fun shootNormalEggs(button: Int, player: Player?) {
player ?: return
val (type, eggType) = listOf(
PenanceType.HEALER to EggTurret.pHealerBtns,
PenanceType.RUNNER to EggTurret.pRunnerBtns,
PenanceType.FIGHTER to EggTurret.pFighterBtns,
PenanceType.RANGER to EggTurret.pRangerBtns,
).firstNotNullOfOrNull { (type, colors) ->
when (button) {
colors.GREEN -> type to EggType.GREEN
colors.RED -> type to EggType.RED
colors.BLUE -> type to EggType.BLUE
else -> type to EggType.YELLOW
}
} ?: if (button == EggTurret.queenBtn) {
PenanceType.QUEEN to EggType.YELLOW
} else return
sendMessage(player, "Shoot ${eggType.action} at $type.")
fireEgg(player, type, eggType)
updateCannonUI(player)
}
fun updateCannonUI(player: Player?){
player ?: return
val eggHopper = getBASession(player)?.eggHopper ?: return
val penances = listOf( EggTurret.pHealer, EggTurret.pRunner, EggTurret.pRanger, EggTurret.pFighter )
penances.forEach { penance ->
val components = listOf(
EggType.GREEN to penance.GREEN,
EggType.RED to penance.RED,
EggType.BLUE to penance.BLUE
)
components.forEach { (eggType, componentId) ->
val value = eggType.getCount(eggHopper)
setInterfaceText(player, value.toString(), EggTurret.ID, componentId)
}
}
setInterfaceText( player, EggType.YELLOW.getCount(eggHopper).toString(), EggTurret.ID, EggTurret.OMEGA )
}
private fun closeTurretIface(player: Player?){
player ?: return
player.interfaceManager.restoreTabs()
player.interfaceManager.openTab(3,Component(149))
}
companion object {
val ALL_EGGS = intArrayOf(Items.GREEN_EGG_10531,Items.RED_EGG_10532,Items.BLUE_EGG_10533,Items.OMEGA_EGG_10537)
fun openTurretIface(player: Player?) {
player ?: return
if (getBASession(player)?.eggHopper?.totalCount() == 0) {
sendDialogueLines(player,"There are no eggs in the hopper.")
return
}
player.interfaceManager.openTab(3,Component(495))
player.interfaceManager.setViewedTab(3)
player.interfaceManager.removeTabs(0, 1, 2, 4, 5, 6,7,8,9,11, 12,13)
}
fun loadEggHopper(player: Player?){
player ?: return
val session = getBASession(player)
val role =session?.team?.getRoleForPlayer(player)
if (role != BarbRole.COLLECTOR) {sendMessage(player,"You need to be a Collector to load eggs.");return}
ALL_EGGS.forEach { loadHopperEggs(player, it) }
//Load hopper emote, Same as place table??
}
fun loadHopperEggs(player: Player?, itemId: Int) {
player ?: return
val session = getBASession(player) ?: return
val hopper = session.eggHopper
val color = hopper.eggColorMap[itemId] ?: return
val current = when (itemId) {
Items.GREEN_EGG_10531 -> hopper.green
Items.RED_EGG_10532 -> hopper.red
Items.BLUE_EGG_10533 -> hopper.blue
Items.OMEGA_EGG_10537 -> hopper.yellow
else -> 0
}
val free = 5 - current
if (free <= 0) { sendMessage(player, "You can't load any more $color eggs."); return }
var loaded = 0
val collectorBag = getCollectorBag(player).bag
val bagCount = collectorBag.getEggCount(itemId)
val fromBag = minOf(free, bagCount)
repeat(fromBag) { collectorBag.container.remove(Item(itemId, 1)); loaded++ }
val remaining = free - loaded
if (remaining > 0) {
val invCount = amountInInventory(player, itemId)
val fromInv = minOf(remaining, invCount)
if (fromInv > 0) {
removeItem(player, Item(itemId, fromInv))
loaded += fromInv
}
}
if (loaded <= 0) { return }
hopper.addEgg(itemId, loaded)
sendMessage(player, "You load the hopper.")
}
fun showEggHopperCount(player: Player?){
player ?: return
val eggsCount = getBASession(player)?.eggHopper
val poison = eggsCount?.green
val explosive = eggsCount?.red
val stun = eggsCount?.blue
sendDialogueLines(player,"The hopper contains:","$poison poison eggs, $explosive explosive eggs","and $stun stun eggs.")
}
//error message CHATBOX "[bold]all of the _PENANCE_ have been killed!"
fun fireEgg(player: Player, type: PenanceType, eggType: EggType){
val session = getBASession(player)
val npcId = session?.currentWaveDefinition?.npcIds?.get(type)
val eggCannon = findClosestNPCIDFromPlayer(player,NPCs.EGG_LAUNCHER_5026,2)
val target = findClosestTargetNPCIDFromNPC(npcId, eggCannon as NPC ,14)
val hopper = session?.eggHopper ?: return
if (target == null) { sendDialogueLines(player,"There are no targetable Penance ${type.name.lowercase().replaceFirstChar { it.uppercase() }} within firing range.") ;return}
if (!eggType.tryConsume(hopper)) {
sendMessage(player, "There are no ${eggType.name.lowercase()} eggs in the hopper.")
return
}
//queueScript { }
player.animate(Animation(5428))
eggCannon.face(target)
eggCannon.animate(Animation(5426))
spawnProjectile(eggCannon,target as Entity,eggType.projectileGFX)
}
}
}
enum class EggType(val consume: EggCannon.EggHopper.() -> Boolean, val action: String, val projectileGFX: Int, val getAmount: EggCannon.EggHopper.() -> Int) {
GREEN({ consumeGreen() }, "poison",GREEN_EGG_PROJECTILE_977, { green }),
RED({ consumeRed() }, "explosive", RED_EGG_PROJECTILE_978, { red }),
BLUE({ consumeBlue() }, "stun", Blue_EGG_PROJECTILE_978, { blue }),
YELLOW({consumeYellow()},"Omega",YELLOW_EGG_PROJECTILE_980, { yellow });
fun tryConsume(hopper: EggCannon.EggHopper): Boolean = hopper.consume()
fun getCount(hopper: EggCannon.EggHopper) = hopper.getAmount()
}
data class EggColors(val GREEN: Int, val RED: Int, val BLUE: Int)
object EggTurret {
const val ID = 495
val pHealer = EggColors(57, 58, 59)
val pHealerBtns = EggColors(11, 12, 13)
val pRunner = EggColors(60, 61, 62)
val pRunnerBtns = EggColors(24, 25, 26)
val pFighter = EggColors(63, 64, 65)
val pFighterBtns = EggColors(37, 38, 39)
val pRanger = EggColors(66, 67, 68)
val pRangerBtns = EggColors(50, 51, 52)
const val OMEGA = 69
const val queenBtn = 56
val close = 72
}
//emote 5428 maybe
/*
https://youtu.be/_EipqTK3Qak?t=106
https://www.youtube.com/watch?v=OJ2BDEM03Dw
entity.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 12)
entity.interfaceManager.restoreTabs()
//hide all tabs but invtenory bag and clan chat tab
//egg explosion 20727
shoot option when no eggs in hoper =
MEssage chatbox-> There are no egs in the hopper.
Dialog message if no monsters in range
"there are no targetable Penance Runners within firing range.
Look-in ->
The hopper contains
0 poison egs, 0 explosive eggs
and 0 stun eggs.
stun eggs
explosive eggs
poison eggs
messages
You load the hopper.
You cant load any more /Egg_type/s.
Picking up wrong egg, "The egg exploded" / 5 damage
getAttribute<Player?>(self, "target", null) // you can set the NPC CANNON a target.
"You put the egg in the bag"
*/

View file

@ -0,0 +1,109 @@
package content.minigame.barbassault.arena.scenery
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.BarbassaultSession.HasUIText
import content.minigame.barbassault.getBASession
import core.api.*
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Scenery
@Initializable
class HornOfGlory : ComponentPlugin() , InteractionListener {
var hornCallLock = false
val CallMap = mapOf(
GloryHorn.CONTROLED_BRONZE_WIND to BarbassaultSession.AttackerStyle.STYLE_1,
GloryHorn.ACCURATE_IRON_WATER to BarbassaultSession.AttackerStyle.STYLE_2,
GloryHorn.AGGRISSIVE_STEEL_EARTH to BarbassaultSession.AttackerStyle.STYLE_3,
GloryHorn.DEFENSIVE_MITHRIL_FIRE to BarbassaultSession.AttackerStyle.STYLE_4,
GloryHorn.POISON_TOFU to BarbassaultSession.PoisonFood.TOFU ,
GloryHorn.POISON_WORMS to BarbassaultSession.PoisonFood.WORMS,
GloryHorn.POISON_MEAT to BarbassaultSession.PoisonFood.MEAT,
GloryHorn.RED_EGG to BarbassaultSession.EggColor.RED,
GloryHorn.GREEN_EGG to BarbassaultSession.EggColor.GREEN,
GloryHorn.BLUE_EGG to BarbassaultSession.EggColor.BLUE,
GloryHorn.FOOD_TOFU to BarbassaultSession.LureFood.TOFU,
GloryHorn.FOOD_CRACKERS to BarbassaultSession.LureFood.CRACKERS,
GloryHorn.FOOD_WORMS to BarbassaultSession.LureFood.WORMS
)
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(484).plugin = this
return this
}
override fun handle(player: Player?,component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int ): Boolean {
player ?: return true
val session = getBASession(player) ?: return true
val toCall = CallMap[button] as HasUIText
if(!hornCallLock) {
session.onHornGloryCall(toCall)
sendChat(player, toCall.hornShout)
player.animate(Animation(5436))
player.lock(6)
hornCallLock = true
//i should replace these with script ques, yes?
GameWorld.Pulser.submit(object : Pulse(6, player) {
override fun pulse(): Boolean {
player.unlock()
hornCallLock = false
return true
}
})
}
return true
}
override fun open(player: Player?, component: Component?) {
super.open(player, component)
player ?: return
getBASession(player)?.updateGloryHornUI()
}
override fun defineListeners() {
on(Scenery.HORN_OF_GLORY_20247, IntType.SCENERY, "Call") { player, _ ->
player.interfaceManager.open(Component(GloryHorn.ID))
return@on true
}
}
}
object GloryHorn {
val ID = 484
//collector
val RED_EGG = 13
val GREEN_EGG = 14
val BLUE_EGG = 15
//attacker
val CONTROLED_BRONZE_WIND = 16
val ACCURATE_IRON_WATER = 17
val AGGRISSIVE_STEEL_EARTH = 18
val DEFENSIVE_MITHRIL_FIRE = 19
//defender
val FOOD_TOFU = 20
val FOOD_WORMS = 21
val FOOD_CRACKERS = 22
//healer
val POISON_TOFU = 23
val POISON_WORMS = 24
val POISON_MEAT = 25
object CallOut {
val defaultText = "---------------------"
const val attacker = 5
const val defender = 6
const val collector = 7
const val healer = 8
}
}

View file

@ -0,0 +1,147 @@
package content.minigame.barbassault.arena.scenery
import content.minigame.barbassault.getBALevels
import core.api.EquipmentSlot
import core.api.addItem
import core.api.getItemFromEquipment
import core.api.sendMessage
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class ItemMachine: InteractionListener {
//Attacker//
val TAKE_RUNES = intArrayOf(Items.CATALYTIC_RUNE_12851,Items.ELEMENTAL_RUNE_12850)
//suppose to use Catalyic and Elemental runes
private val TAKE_ARROWS = intArrayOf(Items.BRONZE_ARROW_882, Items.IRON_ARROW_884, Items.STEEL_ARROW_886, Items.MITHRIL_ARROW_888)
//private val TAKE_RUNES = intArrayOf(Items.AIR_RUNE_556, Items.WATER_RUNE_555, Items.EARTH_RUNE_557, Items.FIRE_RUNE_554, Items.MIND_RUNE_558, Items.CHAOS_RUNE_562, Items.DEATH_RUNE_560, Items.BLOOD_RUNE_565)
//Defender//
private val TAKE_WORMS = Items.WORMS_10515
private val TAKE_CRACKERS = Items.CRACKERS_10513
private val TAKE_TOFU = Items.TOFU_10514
private val baseItems_DEF = intArrayOf(TAKE_WORMS, TAKE_CRACKERS, TAKE_TOFU)
private val STOCK_UP_DEF = IntArray(10 * baseItems_DEF.size) { i -> baseItems_DEF[i % baseItems_DEF.size] }
//Healer//
private val ALL_HEALER_VAILS = intArrayOf(Items.HEALING_VIAL_10546,Items.HEALING_VIAL1_10545,Items.HEALING_VIAL2_10544,Items.HEALING_VIAL3_10543,Items.HEALING_VIAL4_10542)
private val TAKE_VAIL = Items.HEALING_VIAL_10546
private val TAKE_POIS_WORMS = Items.POISONED_WORMS_10540
private val TAKE_POIS_TOFU = Items.POISONED_TOFU_10539
private val TAKE_POIS_MEAT = Items.POISONED_MEAT_10541
private val baseItems_HEAL = intArrayOf(TAKE_POIS_WORMS, TAKE_POIS_TOFU, TAKE_POIS_MEAT)
private val STOCK_UP_HEAL = IntArray(10 * baseItems_HEAL.size) { i -> baseItems_HEAL[i % baseItems_HEAL.size] }
private val ATTACKER_ICON = 10556
private val DEFENDER_ICON = 10558
private val HEALER_ICON = 10559
private val COLLECTOR_ICON = 10557
override fun defineListeners() {
//Attacker//
on(Scenery.ATTACKER_ITEM_MACHINE_20241, IntType.SCENERY, "Take-runes") { player, node ->
handleAddItemMachine(player, node,ATTACKER_ICON, TAKE_RUNES, 300,DispenserType.RUNES)
return@on true
}
on(Scenery.ATTACKER_ITEM_MACHINE_20241, IntType.SCENERY, "Take-arrows") { player, node ->
handleAddItemMachine(player, node,ATTACKER_ICON, TAKE_ARROWS, 300,DispenserType.ARROWS)
return@on true
}
//Defender//
on(Scenery.DEFENDER_ITEM_MACHINE_20242, IntType.SCENERY, "Stock-up") { player, node ->
handleAddItemMachine(player, node,DEFENDER_ICON, STOCK_UP_DEF, 1,DispenserType.FOOD)
return@on true
}
on(Scenery.DEFENDER_ITEM_MACHINE_20242, IntType.SCENERY, "Take-crackers") { player, node ->
handleAddItemMachine(player, node,DEFENDER_ICON, intArrayOf(TAKE_CRACKERS), 5,DispenserType.FOOD)
return@on true
}
on(Scenery.DEFENDER_ITEM_MACHINE_20242, IntType.SCENERY, "Take-tofu") { player, node ->
handleAddItemMachine(player, node,DEFENDER_ICON, intArrayOf(TAKE_TOFU), 5,DispenserType.FOOD)
return@on true
}
on(Scenery.DEFENDER_ITEM_MACHINE_20242, IntType.SCENERY, "Take-worms") { player, node ->
handleAddItemMachine(player, node,DEFENDER_ICON, intArrayOf(TAKE_WORMS), 5,DispenserType.FOOD)
return@on true
}
//Healer//
on(Scenery.HEALER_ITEM_MACHINE_20243, IntType.SCENERY, "Stock-up") { player, node ->
var stockUp = STOCK_UP_HEAL
if (!hasAnyVial(player)) {
stockUp = intArrayOf(TAKE_VAIL) + STOCK_UP_HEAL
}
handleAddItemMachine(player, node,HEALER_ICON, stockUp, 1,DispenserType.POISONED)
return@on true
}
on(Scenery.HEALER_ITEM_MACHINE_20243, IntType.SCENERY, "Take-vial") { player, node ->
if (hasAnyVial(player)) {
sendMessage(player, "You can only carry one healing vial at a time.")
return@on true
}
handleAddItemMachine(player, node,HEALER_ICON, intArrayOf(TAKE_VAIL), 1,DispenserType.VAIL)
return@on true
}
on(Scenery.HEALER_ITEM_MACHINE_20243, IntType.SCENERY, "Take-worms") { player, node ->
handleAddItemMachine(player, node,HEALER_ICON, intArrayOf(TAKE_POIS_WORMS), 5,DispenserType.POISONED)
return@on true
}
on(Scenery.HEALER_ITEM_MACHINE_20243, IntType.SCENERY, "Take-tofu") { player, node ->
handleAddItemMachine(player, node,HEALER_ICON, intArrayOf(TAKE_POIS_TOFU), 5,DispenserType.POISONED)
return@on true
}
on(Scenery.HEALER_ITEM_MACHINE_20243, IntType.SCENERY, "Take-meat") { player, node ->
handleAddItemMachine(player, node,HEALER_ICON, intArrayOf(TAKE_POIS_MEAT), 5,DispenserType.POISONED)
return@on true
}
//Collector//
on(Scenery.COLLECTOR_CONVERTER_21250, IntType.SCENERY, "Convert") { player, _ ->
val collectorLevel = getBALevels(player).col
if (!hasCape(player, COLLECTOR_ICON)) return@on true
//Items.RED_EGG_10532
//Items.BLUE_EGG_10533
//Items.GREEN_EGG_10531
//uhhhhhh
//more research is needed.
return@on true
}
}
private fun handleAddItemMachine(player: Player,node: Node,capeId: Int, items: IntArray, amount: Int,message: DispenserType = DispenserType.GENERIC)
{
if (!hasCape(player, capeId)) return
player.animate(Animation(5419))
player.lock(5)
// theres two emotes for the machine and lock duration
GameWorld.Pulser.submit(object : Pulse(3, player) {
override fun pulse(): Boolean {
addArrayOfItems(player,items,amount)
sendMessage(player, message.message)
player.unlock()
return true
}
})
}
private fun addArrayOfItems(player: Player, ids: IntArray, amount: Int) {
ids.forEach { id -> addItem(player,id,amount) }
}
private fun hasAnyVial(player: Player): Boolean {
return ALL_HEALER_VAILS.any { player.inventory.contains(it,1) }
}
fun hasCape(player: Player, capeId: Int): Boolean {
return getItemFromEquipment(player, EquipmentSlot.CAPE)?.id == capeId
}
private enum class DispenserType(val message: String) {
ARROWS("You take a selection of arrows from the dispenser."),
RUNES("You take a selection of runes from the dispenser."),
FOOD("You fill up your inventory with food from the dispenser."),
POISONED("You fill up your inventory with poisoned food from the dispenser."),
VAIL("You take a vial from the dispenser."),
GENERIC("You take items from the dispenser.")
}
}

View file

@ -0,0 +1,65 @@
package content.minigame.barbassault.bots.roles
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.arena.BarbassaultSession.AttackerStyle
import content.minigame.barbassault.bots.RoleBehavior
import content.minigame.barbassault.bots.core.BABotContext
import content.minigame.barbassault.getBASession
import core.game.node.entity.combat.DeathTask
import core.game.node.entity.Entity
import core.game.node.entity.combat.equipment.WeaponInterface.AttackStyle
import core.game.node.entity.combat.equipment.WeaponInterface.BONUS_SLASH
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
class AttackerBehavior : RoleBehavior {
override fun tick(ctx: BABotContext) {
val bot = ctx.bot
ctx.insultDefender()
useCalledAttackStyle(bot)
if (attackPenance(ctx)) return
if (ctx.poolIdle("BA-Attacker: Idling at pool")) return
}
private fun useCalledAttackStyle(bot: Player) {
val baSession = getBASession(bot) ?: return
val called = (baSession.roleCalls[BarbRole.ATTACKER]?.called ?: AttackerStyle.values().random() ) as AttackerStyle
if (bot.properties.attackStyle.style == called.attackStyle) return
bot.properties.attackStyle = AttackStyle(called.attackStyle, BONUS_SLASH)
}
private fun checkValidTargets(target: NPC): Boolean {
if (!target.isActive()) return false
if (target.skills.lifepoints <= 0 || DeathTask.isDead(target)) return false
val penanceType = target.getAttribute<PenanceType?>("barbass-type", null)
if (penanceType != PenanceType.FIGHTER && penanceType != PenanceType.RANGER) return false
if (!target.getProperties().isMultiZone() && target.inCombat()) return false
if (!target.getDefinition().hasAction("attack")) return false
return true
}
private fun findTargets(entity: Entity): List<Entity> = getBASession(entity)?.sessionNpcs?.filter { checkValidTargets(it) }?.sortedBy { it.location.getDistance(entity.location) }?.take(5)?: emptyList()
fun attackPenance(ctx: BABotContext): Boolean {
val bot = ctx.bot
if (bot.inCombat()) return true
var target = ctx.currentTarget as? Entity
if (target != null && !checkValidTargets(target as NPC)) { ctx.currentTarget = null; target = null }
if (target == null) {
target = findTargets(bot).firstOrNull { !it.inCombat() }?: findTargets(bot).firstOrNull()
if (target == null) return false
ctx.currentTarget = target
}
bot.attack(target)
return true
}
}

View file

@ -0,0 +1,218 @@
package content.minigame.barbassault.bots.core
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BAGroundSpawn
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.PenanceType
import core.game.bots.Script
import core.game.bots.ScriptAPI
import core.game.global.action.PickupHandler
import core.game.interaction.IntType
import core.game.interaction.InteractionListeners
import core.game.node.Node
import core.game.node.entity.player.Player
import core.game.node.item.GroundItemManager
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
class BABotContext( val bot: Player,val api: ScriptAPI ) {
var currentTarget: Node? = null
var script: Script? = null
var session: BarbassaultSession? = null
var role: BarbRole? = null
private var delayTicks = 0
private var defenderInsultNextTick = 0
var tick: Int = 0
fun tickDelay(ticks: Int) { delayTicks = ticks }
fun processDelay(): Boolean {
if (delayTicks > 0) {
delayTicks--
return true
}
return false
}
fun clickDialogueOption(buttonId: Int): Boolean {
val chatbox = bot.interfaceManager.chatbox ?: return false
val action = bot.dialogueInterpreter.actions.removeFirstOrNull()
if (action != null) {
action.handle(bot, buttonId)
bot.interfaceManager.closeChatbox()
bot.dialogueInterpreter.close()
return true
}
if (bot.dialogueInterpreter.dialogue != null) {
bot.dialogueInterpreter.handle(chatbox.id, buttonId)
return true
}
return false
}
fun poolIdle(state: String = "BA-Bot: Idling at pool"): Boolean {
val s = session ?: return false
val base = s.base
val poolArea = ZoneBorders(base.x+27, base.y+18, base.x+24, base.y+16, 0)
if (poolArea.insideBorder(bot.location)) {
return false
}
bot.customState = state
api.walkTo(base.transform(25, 17, 0))
return true
}
fun sameTile(a: Location, b: Location): Boolean {
return a.x == b.x && a.y == b.y && a.z == b.z
}
fun walkToTile(loc: Location, state: String, delay: Int = 2): Boolean {
if (sameTile(bot.location, loc)) return false
bot.customState = state
api.walkTo(loc)
tickDelay(delay)
return true
}
fun findBAGroundItem(itemId: Int, spawnLoc: Location? = null): BAGroundSpawn? {
val s = session ?: return null
return s.BAgroundItems
.filter {
it.id == itemId &&
it.isActive &&
GroundItemManager.getItems().contains(it) &&
(spawnLoc == null || sameTile(it.location, spawnLoc))
}
.minByOrNull { it.location.getDistance(bot.location) }
}
fun findNearestBAGroundItem(itemId: Int, spawnLocs: List<Location>): BAGroundSpawn? {
val s = session ?: return null
return s.BAgroundItems
.filter { item ->
item.id == itemId &&
item.isActive &&
GroundItemManager.getItems().contains(item) &&
spawnLocs.any { sameTile(item.location, it) }
}
.minByOrNull { it.location.getDistance(bot.location) }
}
fun takeBAGroundItem(item: BAGroundSpawn, state: String, delay: Int = 3): Boolean {
if (walkToTile(item.location, state)) return true
bot.customState = state
val takeListener = InteractionListeners.get(item.id, IntType.GROUNDITEM.ordinal, "Take")
?: InteractionListeners.get("Take", IntType.GROUNDITEM.ordinal)
if (takeListener != null) {
takeListener.invoke(bot, item)
} else {
PickupHandler.take(bot, item)
}
tickDelay(delay)
return true
}
fun pickUpBAGroundItem(itemId: Int, spawnLoc: Location, state: String): Boolean {
val item = findBAGroundItem(itemId, spawnLoc) ?: return walkToTile(spawnLoc, state)
return takeBAGroundItem(item, state)
}
fun pickUpNearestBAGroundItem(itemId: Int, spawnLocs: List<Location>, state: String): Boolean {
val item = findNearestBAGroundItem(itemId, spawnLocs)
if (item != null) return takeBAGroundItem(item, state)
val nearestSpawn = spawnLocs.minByOrNull { it.getDistance(bot.location) } ?: return false
return walkToTile(nearestSpawn, state)
}
fun insultDefender() {
val s = session ?: return
if (!s.isPenanceDead(PenanceType.HEALER) || !s.isPenanceDead(PenanceType.RANGER) ||
!s.isPenanceDead(PenanceType.FIGHTER) || s.isPenanceDead(PenanceType.RUNNER)
) {
return
}
if (tick < defenderInsultNextTick) return
val defenderInsults = listOf(
"You defend like Ryan codes Barbass",
"R u asleep?",
"Get szued",
"I've seen better defense from a broken gate",
"Do you even see the runners?",
"My grandma can do better then that!",
"Runner escaped. Again.",
"Nice lure, shame it did nothing.",
"Did you place the food in Lumbridge?",
"The runners thank you for your service.",
"Wrong food, wrong place, classic.",
"You had one job.",
"The runners are speedrunning because of you.",
"I've seen level 5s defend better.",
"Did you forget to pick a call?",
"The runners are literally laughing at you.",
"Blink twice if you're AFK.",
"That runner just waved as it passed.",
"Maybe try feeding them the right bait?",
"You're defending their right to escape.",
"At this rate we'll finish next week.",
"The healer is doing your job too.",
"Even the collector is judging you.",
"Attacker called. Wants their role back.",
"No wonder nobody picks Defender.",
"This is why Defender is always last.",
"You make runners look smart.",
"The trap isn't decorative, you know.",
"Another runner escaped. Impressive.",
"The runners have achieved freedom.",
"I think the runners own the arena now.",
"Outstanding performance. For the runners.",
"The queen will hatch before you finish.",
"Did you learn Defender from YouTube shorts?",
"You lure like a drunk goblin.",
"Food goes on the ground, not in your pocket.",
"The runners aren't supposed to reach the cave.",
"This wave is sponsored by missed runners.",
"Your lure path looks like modern art.",
"The runners are farming YOU for points.",
"Defender? More like Spectator.",
"I've seen bots defend better.",
"The horn gave a call. Did you miss it?",
"Wrong bait any% speedrun.",
"You're boosting the runners' confidence.",
"Defender is not a cosmetic role.",
"Every escaped runner adds 5 minutes.",
"The runners have filed for citizenship.",
"You couldn't defend a sandwich from seagulls.",
"The cave entrance misses you.",
"Maybe the runners are the defenders now.",
"The runners are following YOU.",
"I can hear the healers crying.",
"This is why quick-starts fail.",
"The runner path is not a suggestion.",
"Congratulations, you've unlocked free-range runners.",
"One more escape and they get a pension.",
"Your trap placement scares nobody.",
"Did the runners bribe you?",
"The runners are playing on easy mode.",
"You're the reason Defender guides exist.",
"At least you're consistently bad.",
"The runners have stopped respecting you.",
"Even the penance know you're lost."
)
api.sendChat(defenderInsults.random())
defenderInsultNextTick = tick + (8..20).random()
}
}

View file

@ -0,0 +1,149 @@
package content.minigame.barbassault.bots.roles
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.bots.RoleBehavior
import content.minigame.barbassault.bots.core.BABotContext
import content.minigame.barbassault.arena.scenery.EggCannon
import core.api.amountInInventory
import core.api.freeSlots
import getCollectorBag
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import org.rs09.consts.Scenery
class CollectorBehavior : RoleBehavior {
override fun tick(ctx: BABotContext) {
val bot = ctx.bot
val session = ctx.session ?: return
ctx.script?.preventRandomIdle = true
ctx.insultDefender()
if (isHopperFull(session)) {
bot.customState = "BA-Collector: Hopper full, idling at cannon"
idleAtCannon(ctx)
return
}
if (hasLoadableCarriedEggs(ctx, session)) {
if (loadHopper(ctx)) return
}
val calledEgg = getCalledEgg(session) ?: run {
bot.customState = "BA-Collector: Waiting for attacker call"
idleAtCannon(ctx)
return
}
if (bot.skills.lifepoints <= MIN_SAFE_EGG_HP) {
bot.customState = "BA-Collector: Low HP (${bot.skills.lifepoints}), not picking eggs"
idleAtCannon(ctx)
return
}
if (!canHopperTake(session, calledEgg.itemId)) {
bot.customState = "BA-Collector: Hopper already full for ${calledEgg.name.lowercase()} eggs"
idleAtCannon(ctx)
return
}
if (pickUpCalledEgg(ctx, calledEgg)) return
bot.customState = "BA-Collector: No ${calledEgg.name.lowercase()} eggs nearby"
idleAtCannon(ctx)
}
private fun getCalledEgg(session: BarbassaultSession): BarbassaultSession.EggColor? {
val call = session.roleCalls[BarbRole.COLLECTOR] as? BarbassaultSession.CollectorCall
return call?.called
}
private fun loadHopper(ctx: BABotContext): Boolean {
val hopper = ctx.api.getNearestNode(Scenery.EGG_HOPPER_20264, true) ?: run {
ctx.bot.customState = "BA-Collector: Looking for egg hopper"
return false
}
if (hopper.location.getDistance(ctx.bot.location) > 2) {
ctx.bot.customState = "BA-Collector: Walking to egg hopper"
ctx.api.walkTo(hopper.location)
ctx.tickDelay(2)
return true
}
val session = ctx.session ?: return false
val redBefore = session.eggHopper.red
val carriedBefore = carriedEggCount(ctx, Items.RED_EGG_10532)
ctx.bot.customState = "BA-Collector: Loading red eggs into hopper carried=$carriedBefore hopper=$redBefore"
PRIORITY_HOPPER_EGGS.filter { carriedEggCount(ctx, it) > 0 && canHopperTake(session, it) }
.forEach { EggCannon.loadHopperEggs(ctx.bot, it) }
ctx.bot.customState = "BA-Collector: Loaded red eggs carried=$carriedBefore hopper=$redBefore->${session.eggHopper.red}"
ctx.tickDelay(5)
return true
}
private fun pickUpCalledEgg(ctx: BABotContext, calledEgg: BarbassaultSession.EggColor): Boolean {
if (!canCarryMoreEggs(ctx)) return false
val egg = ctx.findBAGroundItem(calledEgg.itemId) ?: return false
return ctx.takeBAGroundItem(
egg,
"BA-Collector: Picking up ${calledEgg.name.lowercase()} egg id=${egg.id} loc=${egg.location}"
)
}
private fun canCarryMoreEggs(ctx: BABotContext): Boolean {
return getCollectorBag(ctx.bot).bag.freeEggSpace() > 0 || freeSlots(ctx.bot) > 0
}
private fun hasLoadableCarriedEggs(ctx: BABotContext, session: BarbassaultSession): Boolean {
return PRIORITY_HOPPER_EGGS.any { carriedEggCount(ctx, it) > 0 && canHopperTake(session, it) }
}
private fun carriedEggCount(ctx: BABotContext, itemId: Int): Int {
val bot = ctx.bot
return getCollectorBag(bot).bag.getEggCount(itemId) + amountInInventory(bot, itemId)
}
private fun canHopperTake(session: BarbassaultSession, itemId: Int): Boolean {
val hopper = session.eggHopper
return when (itemId) {
Items.GREEN_EGG_10531 -> hopper.green < MAX_HOPPER_EGGS_PER_COLOR
Items.RED_EGG_10532 -> hopper.red < MAX_HOPPER_EGGS_PER_COLOR
Items.BLUE_EGG_10533 -> hopper.blue < MAX_HOPPER_EGGS_PER_COLOR
Items.OMEGA_EGG_10537 -> hopper.yellow < MAX_HOPPER_EGGS_PER_COLOR
else -> false
}
}
private fun isHopperFull(session: BarbassaultSession): Boolean {
val hopper = session.eggHopper
return hopper.green >= MAX_HOPPER_EGGS_PER_COLOR &&
hopper.red >= MAX_HOPPER_EGGS_PER_COLOR &&
hopper.blue >= MAX_HOPPER_EGGS_PER_COLOR
}
private fun idleAtCannon(ctx: BABotContext) {
val cannon = ctx.api.getNearestNode(NPCs.EGG_LAUNCHER_5026, false)
if (cannon != null && cannon.location.getDistance(ctx.bot.location) > 2) {
ctx.api.walkTo(cannon.location)
ctx.tickDelay(2)
return
}
val hopper = ctx.api.getNearestNode(Scenery.EGG_HOPPER_20264, true)
if (hopper != null && hopper.location.getDistance(ctx.bot.location) > 2) {
ctx.api.walkTo(hopper.location)
ctx.tickDelay(2)
}
}
private companion object {
private const val MIN_SAFE_EGG_HP = 6
private const val MAX_HOPPER_EGGS_PER_COLOR = 5
private val PRIORITY_HOPPER_EGGS = intArrayOf(Items.RED_EGG_10532)
}
}

View file

@ -0,0 +1,230 @@
package content.minigame.barbassault.bots.roles
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.LureItems
import content.minigame.barbassault.arena.BAGroundSpawn
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.PenanceType
import content.minigame.barbassault.bots.RoleBehavior
import content.minigame.barbassault.bots.core.BABotContext
import core.api.getWorldTicks
import core.api.setAttribute
import core.game.node.item.Item
import core.game.world.map.Location
import core.game.world.map.RegionManager
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class DefenderBehavior : RoleBehavior {
override fun tick(ctx: BABotContext) {
val bot = ctx.bot
val session = ctx.session ?: return
ctx.script?.preventRandomIdle = true
if (session.isPenanceDead(PenanceType.RUNNER)) {
if (ctx.poolIdle("BA-Defender: Idling at pool")) return
bot.customState = "BA-Defender: Idle at pool"
return
}
val calledFood = getCalledFood(session) ?: run {
bot.customState = "BA-Defender: Waiting for healer call"
return
}
if (stockUp(ctx, session, calledFood)) return
if (repairTrap(ctx, session)) return
if (dropTrapFood(ctx, session, calledFood)) return
if (dropTrailFood(ctx, session, calledFood)) return
if (pickUpTools(ctx)) return
bot.customState = "BA-Defender: Guarding trap"
walkNearTrap(ctx, session)
}
private fun getCalledFood(session: BarbassaultSession): Int? {
val call = session.roleCalls[BarbRole.DEFENDER] as? BarbassaultSession.DefenderCall
return call?.called?.itemId
}
private fun stockUp(ctx: BABotContext, session: BarbassaultSession, calledFood: Int): Boolean {
val bot = ctx.bot
if (!needsMoreFood(session) || bot.inventory.containsAtLeastOneItem(calledFood)) {
return false
}
val machine = ctx.api.getNearestNode(Scenery.DEFENDER_ITEM_MACHINE_20242, true) ?: return false
val option = defenderMachineOption(calledFood) ?: "Stock-up"
bot.customState = "BA-Defender: Taking food id=$calledFood ($option)"
ctx.api.interact(bot, machine, option)
ctx.tickDelay(5)
return true
}
private fun repairTrap(ctx: BABotContext, session: BarbassaultSession): Boolean {
val bot = ctx.bot
if (!bot.inventory.containsAtLeastOneItem(Items.HAMMER_2347) || !bot.inventory.containsAtLeastOneItem(Items.LOGS_11760)) {
return false
}
val trap = RegionManager.getObject(trapLocation(session)) ?: return false
if (trap.id != Scenery.RUNNER_TRAP1_20230 && trap.id != Scenery.BROKEN_TRAP0_20231) {
return false
}
bot.customState = "BA-Defender: Repairing trap"
ctx.api.interact(bot, trap, "Fix")
ctx.tickDelay(6)
return true
}
private fun dropTrapFood(ctx: BABotContext, session: BarbassaultSession, calledFood: Int): Boolean {
val existingTrapFood = countTrapFood(session)
if (existingTrapFood >= TRAP_DROP_COUNT) return false
val botLoc = ctx.bot.location
val inTrapArea = trapTiles(session).any { sameTile(botLoc, it) }
if (!inTrapArea || hasLureFoodAt(session, botLoc)) {
val trapDropLoc = trapDropLocation(session, botLoc)
if (ctx.walkToTile(trapDropLoc, "BA-Defender: Moving to trap bait")) return true
}
if (!dropFood(ctx, session, calledFood, "BA-Defender: Dropping trap bait ${existingTrapFood + 1}/$TRAP_DROP_COUNT")) {
return false
}
return true
}
private fun dropTrailFood(ctx: BABotContext, session: BarbassaultSession, calledFood: Int): Boolean {
val nextTrail = TRAIL_SPOTS
.mapIndexed { index, offset -> index to session.base.transform(offset) }
.firstOrNull { (_, loc) -> !hasLureFoodAt(session, loc) }
?: return false
val (trailIndex, loc) = nextTrail
if (ctx.walkToTile(loc, "BA-Defender: Walking trail ${trailIndex + 1}/${TRAIL_SPOTS.size}")) return true
if (!dropFood(ctx, session, calledFood, "BA-Defender: Dropping trail food ${trailIndex + 1}/${TRAIL_SPOTS.size}")) {
return false
}
return true
}
private fun pickUpTools(ctx: BABotContext): Boolean {
val bot = ctx.bot
val base = ctx.session?.base ?: return false
if (!bot.inventory.containsAtLeastOneItem(Items.HAMMER_2347)) {
return ctx.pickUpBAGroundItem(
Items.HAMMER_2347,
base.transform(32, 42, 0),
"BA-Defender: Getting hammer"
)
}
if (!bot.inventory.containsAtLeastOneItem(Items.LOGS_11760)) {
return ctx.pickUpNearestBAGroundItem(
Items.LOGS_11760,
listOf(base.transform(30, 46, 0), base.transform(29, 47, 0)),
"BA-Defender: Getting logs"
)
}
return false
}
private fun dropFood(ctx: BABotContext, session: BarbassaultSession, foodId: Int, state: String): Boolean {
val bot = ctx.bot
val inventoryItem = bot.inventory.getItem(Item(foodId)) ?: run {
bot.customState = "$state - missing food id=$foodId"
return false
}
val item = Item(foodId, 1)
val droppedItem = item.dropItem
val requiredFood = (session.roleCalls[BarbRole.DEFENDER] as? BarbassaultSession.DefenderCall)?.required?.itemId
val isGoodFood = foodId == requiredFood
if (!bot.inventory.remove(item)) {
bot.customState = "$state - failed remove ${inventoryItem.name} id=${inventoryItem.id}"
return false
}
bot.customState = "$state - ${inventoryItem.name} id=${inventoryItem.id}"
BAGroundSpawn(session, 60, droppedItem, bot.location, isGoodFood).init()
setAttribute(bot, "droppedItem:${droppedItem.id}", getWorldTicks() + 2)
ctx.tickDelay(2)
return true
}
private fun walkNearTrap(ctx: BABotContext, session: BarbassaultSession) {
ctx.walkToTile(trapDropLocation(session, ctx.bot.location), "BA-Defender: Returning to trap")
}
private fun trapLocation(session: BarbassaultSession): Location {
return session.base.transform(45, 34, 0)
}
private fun trapDropLocation(session: BarbassaultSession, from: Location): Location {
val center = trapLocation(session)
if (!hasLureFoodAt(session, center)) return center
return trapTiles(session)
.filter { !hasLureFoodAt(session, it) }
.minByOrNull { it.getDistance(from) }
?: center
}
private fun countTrapFood(session: BarbassaultSession): Int {
val trapTiles = trapTiles(session)
return session.BAgroundItems.count {
it.id in LureItems &&
it.isActive &&
trapTiles.any { tile -> sameTile(it.location, tile) }
}
}
private fun needsMoreFood(session: BarbassaultSession): Boolean {
return countTrapFood(session) < TRAP_DROP_COUNT ||
TRAIL_SPOTS.any { !hasLureFoodAt(session, session.base.transform(it)) }
}
private fun hasLureFoodAt(session: BarbassaultSession, loc: Location): Boolean {
return session.BAgroundItems.any {
it.id in LureItems &&
it.isActive &&
sameTile(it.location, loc)
}
}
private fun sameTile(a: Location, b: Location): Boolean {
return a.x == b.x && a.y == b.y && a.z == b.z
}
private fun trapTiles(session: BarbassaultSession): List<Location> {
return trapLocation(session).get3x3Tiles()
}
private fun defenderMachineOption(foodId: Int): String? {
return when (foodId) {
Items.WORMS_10515 -> "Take-worms"
Items.CRACKERS_10513 -> "Take-crackers"
Items.TOFU_10514 -> "Take-tofu"
else -> null
}
}
private companion object {
private const val TRAP_DROP_COUNT = 2
private val TRAIL_SPOTS = listOf(
Location.create(44, 36, 0),
Location.create(41, 37, 0),
Location.create(36, 37, 0),
Location.create(34, 40, 0)
)
}
}

View file

@ -0,0 +1,196 @@
package content.minigame.barbassault.bots.roles
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.npcs.PENANCE_HEALER_IDS
import content.minigame.barbassault.bots.RoleBehavior
import content.minigame.barbassault.bots.core.BABotContext
import core.game.interaction.IntType
import core.game.interaction.InteractionListeners
import core.game.node.entity.combat.DeathTask
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.node.item.Item
import org.rs09.consts.Items
import org.rs09.consts.Scenery
class HealerBehavior : RoleBehavior {
override fun tick(ctx: BABotContext) {
val bot = ctx.bot
val session = ctx.session ?: return
if (healTeamMate(ctx, session)) return
if (stockUp(ctx)) return
if (fillVial(ctx)) return
if (healSelf(ctx)) return
if (poisonPenanceHealer(ctx, session)) return
if (healAnyTeammate(ctx, session)) return
ctx.insultDefender()
if (ctx.poolIdle("BA-Healer: Idling at pool")) return
bot.customState = "BA-Healer: Idle at pool"
}
private fun healTeamMate(ctx: BABotContext, session: BarbassaultSession): Boolean {
val bot = ctx.bot
val target = session.team.allPlayers()
.filter { it != bot && healthPercent(it) <= TEAM_HEAL_PERCENT && it.skills.lifepoints > 0 }
.minByOrNull { healthPercent(it) }
?: return false
val vial = getBestUsableVial(bot) ?: return false
bot.customState = "BA-Healer: Healing ${target.username} (${target.skills.lifepoints}/${target.skills.maximumLifepoints})"
InteractionListeners.run(vial, target, IntType.PLAYER, bot)
ctx.tickDelay(3)
return true
}
private fun healAnyTeammate(ctx: BABotContext, session: BarbassaultSession): Boolean {
val bot = ctx.bot
val target = session.team.allPlayers()
.filter { it != bot && it.skills.lifepoints > 0 }
.minByOrNull { healthPercent(it) }
?: return false
val vial = getBestUsableVial(bot) ?: return false
if (healthPercent(target) >= 0.95) return false
bot.customState = "BA-Healer: Idle-heal ${target.username}"
InteractionListeners.run(vial, target, IntType.PLAYER, bot)
ctx.tickDelay(2)
return true
}
private fun stockUp(ctx: BABotContext): Boolean {
val bot = ctx.bot
if (hasAnyVial(bot) && POISON_FOODS.any { bot.inventory.containsAtLeastOneItem(it) }) {
return false
}
val machine = ctx.api.getNearestNode(Scenery.HEALER_ITEM_MACHINE_20243, true) ?: return false
val healerCall = ctx.session?.roleCalls?.get(BarbRole.HEALER) as? BarbassaultSession.HealerCall
val calledPoison = healerCall?.called?.itemId
val option = when {
!hasAnyVial(bot) -> "Take-vial"
calledPoison != null && !bot.inventory.containsAtLeastOneItem(calledPoison) -> healerMachineOption(calledPoison)
else -> "Stock-up"
} ?: "Stock-up"
bot.customState = "BA-Healer: Taking supplies ($option)"
ctx.api.interact(bot, machine, option)
ctx.tickDelay(5)
return true
}
private fun fillVial(ctx: BABotContext): Boolean {
val bot = ctx.bot
if (!hasAnyVial(bot) || bot.inventory.containsAtLeastOneItem(Items.HEALING_VIAL4_10542)) {
return false
}
val spring = ctx.api.getNearestNode(Scenery.HEALER_SPRING_20150, true) ?: return false
bot.customState = "BA-Healer: Filling vial"
ctx.api.interact(bot, spring, "Take-from")
ctx.tickDelay(4)
return true
}
private fun healSelf(ctx: BABotContext): Boolean {
val bot = ctx.bot
if (healthPercent(bot) > SELF_HEAL_PERCENT) return false
val spring = ctx.api.getNearestNode(Scenery.HEALER_SPRING_20150, true) ?: return false
bot.customState = "BA-Healer: Drinking from spring"
ctx.api.interact(bot, spring, "Drink-from")
ctx.tickDelay(5)
return true
}
private fun poisonPenanceHealer(ctx: BABotContext, session: BarbassaultSession): Boolean {
val bot = ctx.bot
val poison = (session.roleCalls[BarbRole.HEALER] as? BarbassaultSession.HealerCall)?.called
?: return false
if (!bot.inventory.containsAtLeastOneItem(poison.itemId)) {
val machine = ctx.api.getNearestNode(Scenery.HEALER_ITEM_MACHINE_20243, true) ?: return false
val option = healerMachineOption(poison.itemId) ?: "Stock-up"
bot.customState = "BA-Healer: Restocking ${poison.ui} ($option)"
ctx.api.interact(bot, machine, option)
ctx.tickDelay(5)
return true
}
val target = session.sessionNpcs
.filter { isValidPenanceHealer(it) }
.minByOrNull { it.location.getDistance(bot.location) }
?: return false
bot.customState = "BA-Healer: Poisoning ${target.name} with ${poison.ui}"
ctx.api.useWith(bot, poison.itemId, target)
ctx.tickDelay(3)
return true
}
private fun getBestUsableVial(player: Player): Item? {
return USABLE_HEALING_VIALS
.firstOrNull { player.inventory.containsAtLeastOneItem(it) }
?.let { player.inventory.getItem(Item(it)) }
}
private fun hasAnyVial(player: Player): Boolean {
return ALL_HEALING_VIALS.any { player.inventory.containsAtLeastOneItem(it) }
}
private fun healthPercent(player: Player): Double {
val max = player.skills.maximumLifepoints
if (max <= 0) return 1.0
return player.skills.lifepoints.toDouble() / max.toDouble()
}
private fun isValidPenanceHealer(npc: NPC): Boolean {
return npc.id in PENANCE_HEALER_IDS &&
npc.isActive &&
npc.skills.lifepoints > 0 &&
!DeathTask.isDead(npc)
}
private fun healerMachineOption(foodId: Int): String? {
return when (foodId) {
Items.POISONED_TOFU_10539 -> "Take-tofu"
Items.POISONED_WORMS_10540 -> "Take-worms"
Items.POISONED_MEAT_10541 -> "Take-meat"
else -> null
}
}
private companion object {
private const val TEAM_HEAL_PERCENT = 0.65
private const val SELF_HEAL_PERCENT = 0.40
private val ALL_HEALING_VIALS = intArrayOf(
Items.HEALING_VIAL_10546,
Items.HEALING_VIAL1_10545,
Items.HEALING_VIAL2_10544,
Items.HEALING_VIAL3_10543,
Items.HEALING_VIAL4_10542
)
private val USABLE_HEALING_VIALS = intArrayOf(
Items.HEALING_VIAL4_10542,
Items.HEALING_VIAL3_10543,
Items.HEALING_VIAL2_10544,
Items.HEALING_VIAL1_10545
)
private val POISON_FOODS = intArrayOf(
Items.POISONED_TOFU_10539,
Items.POISONED_WORMS_10540,
Items.POISONED_MEAT_10541
)
}
}

View file

@ -0,0 +1,87 @@
package content.minigame.barbassault.bots
import content.minigame.barbassault.arena.BarbassaultState
import content.minigame.barbassault.bots.core.BABotContext
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.lobby.BALobby
import content.minigame.barbassault.lobby.BALobby.Companion.QUICK_START
import core.game.world.map.Location
import org.rs09.consts.Scenery
class LobbyBehavior : RoleBehavior {
private enum class LobbyState { GET_TO_BA, FINDING_ROOM, WAITING_IN_ROOM,IN_ARENA }
override fun tick(ctx: BABotContext) {
val state = state(ctx)
ctx.bot.customState = "BA-Lobby: $state"
ctx.bot.debug("$state")
when (state) {
LobbyState.GET_TO_BA -> getToBA(ctx)
LobbyState.FINDING_ROOM -> enterRoom(ctx)
LobbyState.WAITING_IN_ROOM -> waitingInRoom(ctx)
LobbyState.IN_ARENA -> return
}
}
private fun state(ctx: BABotContext): LobbyState {
val loc = ctx.bot.location
val session = getBASession(ctx.bot)
ctx.bot.debug("loc=$loc waiting=${isWaitingArea(loc)} halls=${isInHalls(loc)} session=${session != null}")
return when {
(session?.state == BarbassaultState.IN_ARENA) -> LobbyState.IN_ARENA
(session?.state == BarbassaultState.END) -> LobbyState.IN_ARENA
(session?.state == BarbassaultState.COMPLETE) -> LobbyState.IN_ARENA
isWaitingQuick(loc) -> LobbyState.WAITING_IN_ROOM
isWaitingArea(loc) && (session != null) -> LobbyState.WAITING_IN_ROOM
isInHalls(loc) -> LobbyState.FINDING_ROOM
else -> LobbyState.GET_TO_BA
}
}
private fun waitingInRoom(ctx: BABotContext) {
if (ctx.tick % 12 == 0 && !ctx.bot.walkingQueue.isMoving) {
ctx.api.walkTo(QUICK_START.randomWalkableLoc)
ctx.tickDelay(3)
return
}
if (ctx.tick % 150 == 0) {
ctx.api.sendChat("Cmon, we need more people!!")
}
}
private fun getToBA(ctx: BABotContext) {
ctx.bot.teleport(BALobby.MAIN_LOBBY.randomWalkableLoc)
ctx.tickDelay(4)
}
private fun enterRoom(ctx: BABotContext) {
if (ctx.bot.interfaceManager.hasChatbox()) {
ctx.clickDialogueOption(6)
ctx.tickDelay(8)
return
}
val quickDoor = ctx.api.getNearestNode(Scenery.DOOR_20209, true)
ctx.api.interact(ctx.bot, quickDoor, "Pass")
ctx.tickDelay(16)
}
private fun isInHalls(location: Location): Boolean {
return BALobby.isInHalls(location)
}
private fun isWaitingQuick(location: Location): Boolean {
return QUICK_START.insideBorder(location)
}
private fun isWaitingArea(location: Location): Boolean {
return BALobby.isWaitingArea(location)
}
}

View file

@ -0,0 +1,100 @@
package content.minigame.barbassault.bots
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultState
import content.minigame.barbassault.bots.core.BABotContext
import content.minigame.barbassault.bots.roles.AttackerBehavior
import content.minigame.barbassault.bots.roles.CollectorBehavior
import content.minigame.barbassault.bots.roles.DefenderBehavior
import content.minigame.barbassault.bots.roles.HealerBehavior
import content.minigame.barbassault.getBARoleForPlayer
import content.minigame.barbassault.getBASession
import core.game.bots.AIPlayer
import core.game.bots.CombatBotAssembler
import core.game.bots.PvMBots
import core.game.bots.Script
import core.game.bots.ScriptAPI
import core.game.container.impl.EquipmentContainer
import core.game.global.action.EquipHandler
import core.game.world.map.Location
interface RoleBehavior {
fun tick(ctx: BABotContext)
}
private class SmartBABotBrain(private val ctx: BABotContext) {
private val lobby = LobbyBehavior()
private val attacker = AttackerBehavior()
private val collector = CollectorBehavior()
private val defender = DefenderBehavior()
private val healer = HealerBehavior()
private var tickTimer = 0
fun tick() {
tickTimer++
ctx.tick = tickTimer
if (ctx.processDelay()) return
ctx.session = getBASession(ctx.bot)
if (ctx.session?.state == BarbassaultState.IN_ARENA) {
ctx.role = getBARoleForPlayer(ctx.bot)
when (ctx.role) {
BarbRole.ATTACKER -> attacker.tick(ctx)
BarbRole.COLLECTOR -> collector.tick(ctx)
BarbRole.DEFENDER -> defender.tick(ctx)
BarbRole.HEALER -> healer.tick(ctx)
null -> return
}
return
}
lobby.tick(ctx)
}
}
class SmartBABot(l: Location) : PvMBots(l) {
private val ctx = BABotContext(this, ScriptAPI(this))
private val brain = SmartBABotBrain(ctx)
init {
(this as AIPlayer).fullRestore()
CombatBotAssembler().gearPCiMeleeBot(this)
val cape = equipment.get(EquipmentContainer.SLOT_CAPE)
if (cape != null) {
EquipHandler.unequip(this, EquipmentContainer.SLOT_CAPE, cape.id)
}
}
override fun tick() {
brain.tick()
super.tick()
}
}
class SmartBAPlayerScript : Script() {
private lateinit var brain: SmartBABotBrain
init {
endDialogue = false
preventRandomIdle = true
}
override fun init(isPlayer: Boolean) {
super.init(isPlayer)
val ctx = BABotContext(bot, scriptAPI)
ctx.script = this
brain = SmartBABotBrain(ctx)
}
override fun tick() {
brain.tick()
}
override fun newInstance(): Script {
return SmartBAPlayerScript().also { it.bot = this.bot }
}
}

View file

@ -0,0 +1,185 @@
package content.minigame.barbassault.bots.oldbot
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultState
import content.minigame.barbassault.getBARoleForPlayer
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.lobby.BALobby
import content.minigame.barbassault.lobby.BarbassQuickWaveActivity
import core.api.forceMove
import core.api.getRegionBorders
import core.api.log
import core.game.bots.AIPlayer
import core.game.bots.CombatBotAssembler
import core.game.bots.PvMBots
import core.game.bots.ScriptAPI
import core.game.global.action.EquipHandler
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location
import core.tools.Log
import org.rs09.consts.Scenery
class BABot(l: Location) : PvMBots(legitimizeLocation(l)) {
init {
(this as AIPlayer).fullRestore()
CombatBotAssembler().gearPCiMeleeBot(this)
val cape: Item? = equipment.get(1)
if (cape != null) {
EquipHandler.Companion.unequip(this, 1, cape.id)
}
}
private val combatHandler = BACombatState(this)
private var tickTimer = 0
private var moveTimer = 0
var hasVendingItems = false
var scriptAPI: ScriptAPI? = null
enum class State { GET_TO_BA, OUTSIDE_ROOM, ENTER_ROOM, WAITING_IN_ROOM, PLAY_GAME }
override fun tick() {
super.tick()
tickTimer++
moveTimer--
customState = state.name
if (moveTimer > 0) {
return
}
when (state) {
State.GET_TO_BA -> getToBA()
State.OUTSIDE_ROOM -> outsideRoom()
State.ENTER_ROOM -> enterRoom()
State.WAITING_IN_ROOM -> waitingInRoom()
State.PLAY_GAME -> playGame()
}
}
private val state: State
get() {
val session = getBASession(this)
if (session?.state == BarbassaultState.IN_ARENA) {
return State.PLAY_GAME
}
if (isWaitingArea(location)) {
return State.WAITING_IN_ROOM
}
if (getClosestNodeWithEntry(2, Scenery.DOOR_20209) != null) {
return State.ENTER_ROOM
}
if (isInLobbyOnly(location)) {
return State.OUTSIDE_ROOM
}
return State.GET_TO_BA
}
private fun getToBA() {
teleport(BALobby.Companion.MAIN_LOBBY.randomWalkableLoc)
moveTimer = 3
}
fun useRoleVendingMachine() {
if (hasVendingItems) return
val roleMachine = scriptAPI?.getNearestNode(getBARoleForPlayer(this)!!.machineId, true)
if (roleMachine?.id == Scenery.COLLECTOR_CONVERTER_21250) return
scriptAPI?.interact(this, roleMachine, "Stock-up")
}
private fun outsideRoom() {
val quickDoor = getClosestNodeWithEntry(60, Scenery.DOOR_20209) ?: return
quickDoor.interaction.handle(this, quickDoor.interaction[0])
moveTimer = 2
}
private fun enterRoom() {
val quickDoor = getClosestNodeWithEntry(2, Scenery.DOOR_20209) ?: return
quickDoor.interaction.handle(this, quickDoor.interaction[0])
forceMove(this, location, location.transform(0, -1, 0), 25, 60, null, 819) {
BarbassQuickWaveActivity.Companion.addToQueue(this, null)
combatHandler.walkTo(BALobby.Companion.QUICK_START.randomWalkableLoc)
}
moveTimer = 4
}
private fun waitingInRoom() {
if (tickTimer % 50 == 0) {
combatHandler.chatter()
}
}
private fun playGame() {
when (getBARoleForPlayer(this)) {
BarbRole.ATTACKER -> combatHandler.fightNPCs()
BarbRole.DEFENDER -> combatHandler.handleDefender()
BarbRole.COLLECTOR -> combatHandler.handleCollector()
BarbRole.HEALER -> combatHandler.handleHealer()
null -> return
}
}
fun isInLobbyOnly(location: Location): Boolean {
return getRegionBorders(10322).insideBorder(location)
&& BALobby.Companion.waveLobbies.none {
it != BALobby.Companion.MAIN_LOBBY
&& it != BALobby.Companion.QUICK_START
&& it.insideBorder(location)
}
}
private fun isWaitingArea(location: Location): Boolean {
hasVendingItems = false
return BALobby.Companion.QUICK_START.insideBorder(location) || BALobby.Companion.waveLobbies.any { it.insideBorder(location) && it != BALobby.Companion.MAIN_LOBBY }
}
fun debugthis(info : Any) {
log(this.javaClass, Log.FINE, state.toString())
}
override fun AttackNpcsInRadius(bot: Player, radius: Int): Boolean {
if (bot.inCombat()) return true
val creatures = FindTargets(bot, radius)
if (creatures.isEmpty()) return false
for (npc in creatures) {
if (!npc.inCombat()) {
bot.attack(npc)
return true
}
}
return false
}
override fun FindTargets(entity: Entity, radius: Int): MutableList<Entity> {
val localNPCs = getBASession(entity)?.sessionNpcs ?: return mutableListOf()
return localNPCs
.asSequence()
.filter(::checkValidTargets)
.sortedBy { it.location.getDistance(entity.location) }
.take(5)
.toMutableList()
}
companion object {
fun legitimizeLocation(l: Location): Location {
return l
}
}
}

View file

@ -0,0 +1,99 @@
package content.minigame.barbassault.bots.oldbot
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.getBARoleForPlayer
import content.minigame.barbassault.getBASession
import core.game.interaction.MovementPulse
import core.game.node.entity.combat.equipment.WeaponInterface
import core.game.world.GameWorld
import core.game.world.map.Location
import core.game.world.map.path.Pathfinder
import core.tools.RandomFunction
class BACombatState(private val bot: BABot) {
fun fightNPCs() {
bot.customState = "Fight NPCs"
bot.hasVendingItems = true
bot.AttackNpcsInRadius(bot,25)
//use Called AttackStyle
val baSession = getBASession(bot) ?: return
val called = (baSession.roleCalls[BarbRole.ATTACKER]?.called ?: BarbassaultSession.AttackerStyle.values().random() ) as BarbassaultSession.AttackerStyle
bot.properties.attackStyle = WeaponInterface.AttackStyle(called.attackStyle, WeaponInterface.BONUS_SLASH)
}
fun handleDefender() {
bot.customState = "Lure Runners"
bot.useRoleVendingMachine()
val session = getBASession(bot) ?: return
randomWalk(session.region.borders.randomWalkableLoc, 3)
//used Called Lure
}
fun handleCollector() {
bot.customState = "Collecting Eggs"
bot.hasVendingItems = true
val session = getBASession(bot) ?: return
randomWalk(session.region.borders.randomWalkableLoc, 3)
//only target Called Eggs
}
fun handleHealer() {
bot.customState = "Healing"
bot.useRoleVendingMachine()
val session = getBASession(bot) ?: return
//if healing vial Empty Go fill.
randomWalk(session.region.borders.randomWalkableLoc, 2)
//Only use Called poisonFood.
}
fun chatter() {
val role = getBARoleForPlayer(bot)
val messages = when (role) {
BarbRole.ATTACKER -> listOf("Call pls","Wrong style?","Attacking!")
BarbRole.HEALER -> listOf("Need poison","Healing!","Healer here","Call pls")
BarbRole.DEFENDER -> listOf("Dropping food","Come here runners","Call pls")
BarbRole.COLLECTOR -> listOf("Eggs!","Wrong egg?","Call pls")
else -> listOf("Lag...","Nice","Oops")
}
bot.sendChat(messages.random())
}
fun randomWalk(center: Location, radius: Int) {
if (!bot.hasVendingItems) return
if (bot.walkingQueue.isMoving) return
val destination = center.transform(
RandomFunction.random(-radius, radius),
RandomFunction.random(-radius, radius),
0
)
walkTo(destination)
}
fun walkTo(destination: Location) {
val diffX = destination.x - bot.location.x
val diffY = destination.y - bot.location.y
GameWorld.Pulser.submit(
object : MovementPulse(
bot,
bot.location.transform(diffX, diffY, 0),
Pathfinder.SMART
) {
override fun pulse(): Boolean {
return true
}
}
)
}
}

View file

@ -0,0 +1,140 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.getBAWave
import content.minigame.barbassault.lobby.BarbTeamManager.abandonTeam
import content.minigame.barbassault.lobby.BarbTeamManager.joinTeam
import core.api.closeInterface
import core.api.sendMessage
import core.api.setInterfaceText
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
@Initializable
class AcceptRoleInterface : ComponentPlugin() {
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(AcceptRole.ID).plugin = this
return this
}
override fun handle(player: Player,component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int ): Boolean {
when (button) {
AcceptRole.AcceptButton, AcceptRole.AcceptButtonText -> { handleAccept(player); return true }
AcceptRole.DeclineButton, AcceptRole.DeclineButtonText -> { handleDecline(player); return true }
}
val slotIndex = AcceptRole.team.indexOfFirst { it.remove == button }
if (slotIndex != -1) { handleRemove(player!!, slotIndex); return true }
return true
}
override fun open(player: Player?, component: Component?) {
super.open(player, component)
var isopen = true
player ?: return
val team = getBASession(player)?.team
val recruit = team?.recruit?.player
component?.setCloseEvent{ _, _ -> if (recruit != null && isopen) { closeInterface(recruit); isopen = false }; return@setCloseEvent true }
for (slotIndex in 0 until BarbTeam.MAX_SLOTS) {
val uiSlot = AcceptRole.team[slotIndex]
val partySlot: PartySlot? = team?.getSlot(slotIndex)
val slotPlayer: Player? = partySlot?.player
if (slotPlayer != null) {
val formattedRole = getBASession(player)?.team?.getRoleForPlayer(slotPlayer)?.toString()?.lowercase()?.replaceFirstChar { it.uppercase() }?: AcceptRole.defaultText
val wave = getBAWave(slotPlayer)
setInterfaceText(player, slotPlayer.name.capitalizeWords(), AcceptRole.ID, uiSlot.name )
setInterfaceText(player, formattedRole, AcceptRole.ID, uiSlot.role )
setInterfaceText(player, wave.toString(), AcceptRole.ID, uiSlot.wave )
} else {
setInterfaceText(player, AcceptRole.defaultText, AcceptRole.ID,uiSlot.name )
setInterfaceText(player, AcceptRole.defaultText, AcceptRole.ID,uiSlot.role )
setInterfaceText(player, "0", AcceptRole.ID,uiSlot.wave )
}
}
}
private fun handleAccept(player: Player) {
val team = getBASession(player)?.team
val recruit = team?.recruit
if (!recruit?.accept!!) {
sendMessage(player,"The other player hasn't confirmed their role yet.")
return
}
val selectedRole = recruit.role ?: return
val roleName = selectedRole.name.lowercase().replaceFirstChar { it.uppercase() }
val rPlayer = recruit.player
if (joinTeam(rPlayer, selectedRole)) {
rPlayer.sendMessage("You have joined as ${roleName}.")
team.recruit = null
closeInterface(player)
}
return
}
private fun handleDecline(player: Player?) {
closeInterface(player!!)
/**
val leader = player.baSession?.team?.getLeader()?.player
if (leader != null && leader != player) {
closeInterface(leader)
}**/
}
private fun handleRemove(player: Player, slotIndex: Int){
val team = getBASession(player)?.team
val follower = team?.getSlot(slotIndex)?.player ?: return
val uiSlot = AcceptRole.team[slotIndex]
sendMessage(player,"You removed the person from the team.")
setInterfaceText(player, AcceptRole.defaultText, AcceptRole.ID,uiSlot.name )
setInterfaceText(player, AcceptRole.defaultText, AcceptRole.ID,uiSlot.role )
setInterfaceText(player, "0", AcceptRole.ID,uiSlot.wave )
abandonTeam(follower)
}
companion object {
fun updateRecruitData(player: Player){
val team = getBASession(player)?.team
val leader = team?.getLeader()?.player ?: return
val recruit = team.recruit
val displayName = recruit?.role?.name?.lowercase()?.replaceFirstChar { it.uppercase() } ?: "Unknown"
val level = recruit?.roleLevel()
setInterfaceText(leader, displayName, AcceptRole.ID,AcceptRole.Status.selectedRole )
setInterfaceText(leader, level.toString(), AcceptRole.ID,AcceptRole.Status.levelRole )
}
fun resetWaitingStatus(player: Player){
val leader = getBASession(player)?.team?.getLeader()?.player
setInterfaceText(leader!!, AcceptRole.waiting, AcceptRole.ID,AcceptRole.Status.selectedRole )
setInterfaceText(leader, AcceptRole.waiting, AcceptRole.ID,AcceptRole.Status.levelRole )
}
}
}
object AcceptRole {
const val ID = 492
const val defaultText = "none set"
const val waiting = "Waiting ..."
const val AcceptButtonText = 4
const val DeclineButtonText = 2
const val AcceptButton = 35
const val DeclineButton = 3
data class Slot(val name: Int, val role: Int,val remove: Int, val wave: Int)
val team = listOf(
Slot(15,20,-1,26),
Slot(16,21,47,31),
Slot(17,22,27,32),
Slot(18,23,28,33),
Slot(19,24,29,34),
)
object Status {
const val selectedRole = 6
const val levelRole = 46
}
}

View file

@ -0,0 +1,393 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.arena.BarbassaultState
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.getBAWave
import content.minigame.barbassault.lobby.BarbTeamManager.abandonTeam
import content.minigame.barbassault.lobby.BarbTeamManager.createTeam
import content.minigame.barbassault.lobby.BarbTeamManager.disbandTeam
import content.minigame.barbassault.lobby.BarbassQuickWaveActivity.Companion.addToQueue
import content.minigame.barbassault.setBASession
import content.minigame.barbassault.setBAWave
import core.api.*
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.impl.PulseManager
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import org.rs09.consts.Items
import org.rs09.consts.Scenery
import org.rs09.consts.Sounds
//https://www.youtube.com/watch?v=0d5I97HLUEs ending of video shows we are allowed capes in lap but not wave rooms
//"-- Next wave no longer starting"
// The game ended early because one of your team-mated died.
//private var activity: BarbassaultActivity? = null
class BALobby : InteractionListener, MapArea {
override fun defineAreaBorders(): Array<ZoneBorders> {
return areaBorders
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.FOLLOWERS)
}
override fun areaEnter(entity: Entity) {
PulseManager.cancelDeathTask(entity)
val enteredZone = areaBorders.firstOrNull { it.insideBorder(entity.location) }
if (enteredZone == QUICK_START) { return }
if (enteredZone == REGION_10322) { return }
//updatePlayerWave(entity)
openOverlay(entity as Player, 256)
}
override fun areaLeave(entity: Entity, logout: Boolean) {
if (entity is Player) {
entity.interfaceManager.closeOverlay()
}
}
//was used in early development.
fun updatePlayerWave(entity: Entity) {
val player = entity as? Player ?: return
val location = player.location
if (QUICK_START.insideBorder(location)) {
setBAWave(entity,1)
return
}
for ((index, lobby) in waveLobbies.withIndex()) {
if (lobby.insideBorder(location)) {
if (lobby != MAIN_LOBBY) { // MAIN_LOBBY should not assign a wave number
setBAWave(entity,index)
}
return
}
}
}
private fun capeCheck(player: Player): Boolean {
val wornCape = getItemFromEquipment(player, EquipmentSlot.CAPE)?.id ?: -1
if (wornCape != -1) {
//todo find authentic "no capes allowed" message
sendMessage(player,"Capes are not permitted.")
return false
}
return true
}
//attacker 20561 : ch8
//defender 20566 : ch10
//collector 20563 : ch13
//healer 20569 : ch14
override fun defineListeners() {
onUseWithPlayer(Items.SCROLL_10512) { leader, _, node ->
val follower = node.asPlayer()
val session = getBASession(leader)
val team = session?.team
if (team == null) {
leader.debug("[DEBUG] No active session found.")
return@onUseWithPlayer false
}
getBASession(follower)?.let { return@onUseWithPlayer false } // already on a team
team.recruit = PendingRecruit(follower)
setBASession(follower, session)
//leader.sendMessage(team.recruit?.player?.username ?: "null")
// joinTeam(follower,BarbRole.DEFENDER)
openInterface(follower, 493)
openInterface(leader, 492)
return@onUseWithPlayer true
}
on(Items.SCROLL_10512, IntType.ITEM, "Read") { player, _ ->
//https://youtu.be/GYh6JPKNOSA?t=233 -- I know 2017, but only example for read scroll iv found.
// ima cry, that YT account got deleted.
waveAdvance(player)
return@on true
}
on(Items.SCROLL_10512, IntType.ITEM, "Write-Role") { player, _ ->
sendDialogueOptions(player, "Which role would you like to perform?","Attacker", "Defender","Collector","Healer","Cancel")
addDialogueAction(player) { _ , buttonId -> writeRole(player,buttonId) }
return@on true
}
on(Items.SCROLL_10512, IntType.ITEM, "Clear") { player, _ ->
if (getBASession(player)?.team != null) return@on true
createTeam(player)
return@on true
}
on(Items.SCROLL_10512, IntType.ITEM, "Destroy") { player, item ->
player.dialogueInterpreter.sendDestroyItem(item.id, item.name)
addDialogueAction(player) { _, button ->
if (button == 3) {
if (removeItem(player, item)) {
playAudio(player, Sounds.DESTROY_OBJECT_2381)
abandonTeam(player)
}
}
}
return@on true
}
on(Scenery.LADDER_20193, IntType.SCENERY, "Climb-down") { player, _ ->
val session = getBASession(player)
val team = session?.team
if (session == null) { player.debug("[DEBUG] You are not in a session!"); return@on true }
if (team?.isLeader(player) == false) return@on true
//todo uncomment when goes to test
//if (team?.isFull() == false) return@on true
if (session.state != BarbassaultState.PARTY_CREATION) { player.debug("[DEBUG] Your team is already starting or in the arena!"); return@on true }
closeOverlay(player) // close party
openOverlay(player, 494) // open countdown
session.beginStartingPhase()
return@on true
}
on(Scenery.DOOR_20209, IntType.SCENERY, "Pass") { player, node ->
if (!capeCheck(player)) return@on false
// todo Not allowed to enter QuickStart with Scroll
if (!QUICK_START.insideBorder(player.location)) {
if (getBAWave(player) != 1) {
sendDialogueOptions(player,"Are you sure you wish to reset your wave progress??","Yes","No")
addDialogueAction(player) { _, buttonId ->
if (buttonId == 2) {
setBAWave(player, 1)
handlePlayerOptionDoor(player) { role ->
handleWaveDoor(player, node, 489)
addToQueue(player, role)
}
}
}
} else {
handlePlayerOptionDoor(player) { role ->
handleWaveDoor(player, node, 489)
addToQueue(player, role)
}
}
} else {
handleWaveDoor(player, node, 489)
}
return@on true
}
on(BARBDOORS, IntType.SCENERY, "Pass") { player, node ->
//todo find authentic message no capes
if (!capeCheck(player)) { sendMessage(player,"You are not allowed to wear capes."); return@on true }
handleWaveDoor(player, node,256)
return@on true
}
on(Scenery.SCROLL_TABLE_20149, IntType.SCENERY, "Take-from") { player, _ ->
//https://www.youtube.com/watch?v=bFw2os1aRvk
// okay we dont create a session untill we write our name on the scroll.
if(!player.inventory.containsItem(Item(Items.SCROLL_10512, 1)) ) {
//do you wish to take a scroll? yes ? no
sendDialogueOptions(player, "Take a recruitment scroll?", "Yes", "No")
addDialogueAction(player) { _, buttonId ->
when (buttonId) {
2 -> {
if (player.inventory.add(Item(Items.SCROLL_10512, 1))) {
player.sendMessage("You take scroll from table")
} else {
player.sendMessage("You dont have enough inventory space")
}
}
}
}
} else {
player.sendMessage("You already have a scroll")
}
return@on true
}
}
//DOOR_20209
// [Title][red] "Which role would you like to perform?"
// [0] "Attacker"
// [1] "Defender"
// [2] "Collector"
// [3] "Healer"
// [4] "I'll be any role!"
fun getRoleFromButton(button: Int): BarbRole? {
return when (button) {
2 -> BarbRole.ATTACKER
3 -> BarbRole.DEFENDER
4 -> BarbRole.COLLECTOR
5 -> BarbRole.HEALER
else -> null
}
}
fun writeRole(player: Player, buttonId: Int) {
if (buttonId == 6) return
val playerRole = getRoleFromButton(buttonId) ?: return
if (getBASession(player) == null) {createTeam(player)}
val team = getBASession(player)?.team ?: return
team.setRoleForPlayer(player, playerRole)
BarbTeamManager.updateTeam(team)
sendMessage(player, "You've set your role to ${playerRole.name.lowercase().replaceFirstChar { it.uppercase() }}")
}
fun handlePlayerOptionDoor(player: Player, roomRoleOptions:(BarbRole?) -> Unit){
sendDialogueOptions(player,"Which role would you like to perform?","Attacker", "Defender","Collector","Healer","I'll be any role!")
addDialogueAction(player) { _, buttonId -> roomRoleOptions(getRoleFromButton(buttonId)) }
}
fun handleWaveDoor(player: Player, node: Node,iface: Int){
val doorLoc = node.location
val playerLoc = player.location
if (getBAWave(player) != BARBWAVEDOOR[node.id]) return
val insideRoom = isWaitingArea(playerLoc)
val moveY = if (insideRoom) 1 else -1
//ToDo fix walking trough wall when Using door from side.
forceMove(player, playerLoc, playerLoc.transform(0, moveY, 0), 25, 60, null, 819) {
if (insideRoom) {
//player.baSession?.cancleStartingPhase()
abandonTeam(player)
closeOverlay(player)
if (node.id == Scenery.DOOR_20209) { BarbassQuickWaveActivity.removeQuickQue(player) } // needs to happen sooner
} else {
openOverlay(player, iface)
}
}
}
companion object {
fun isWaitingArea(location: Location): Boolean {
return QUICK_START.insideBorder(location) || waveLobbies.any { it.insideBorder(location) && it != MAIN_LOBBY }
}
fun isInHalls(location: Location): Boolean {
return getRegionBorders(10322).insideBorder(location) &&
!QUICK_START.insideBorder(location) &&
waveLobbies.none {
it != MAIN_LOBBY && it.insideBorder(location)
}
}
fun TeamToLobby(team: BarbTeam, wave: Int) {
if (wave in 0..waveLobbies.size) {
val targetLobby = waveLobbies[wave]
for (player in team.allPlayers()) {
PulseManager.cancelDeathTask(player)
teleport(player, targetLobby.randomWalkableLoc)
}
}
}
fun waveAdvance(player: Player) {
val wave = getBAWave(player)
if (wave in 1..waveLobbies.size) {
if (wave == 10) {setBAWave(player,1)}
} else {
sendMessage(player,"Invalid wave number: $wave. PLEASE REPORT THIS!")
}
}
// Locations
val QUICK_START: ZoneBorders = ZoneBorders(2595, 5278, 2610, 5271, 0)
val MAIN_LOBBY: ZoneBorders = ZoneBorders(2588, 5266, 2597, 5261, 0)
val WAVE_LOBBY1: ZoneBorders = ZoneBorders(2576, 5298, 2583, 5291, 0)
val WAVE_LOBBY2: ZoneBorders = ZoneBorders(2584, 5298, 2591, 5291, 0)
val WAVE_LOBBY3: ZoneBorders = ZoneBorders(2595, 5298, 2602, 5291, 0)
val WAVE_LOBBY4: ZoneBorders = ZoneBorders(2603, 5298, 2610, 5291, 0)
val WAVE_LOBBY5: ZoneBorders = ZoneBorders(2576, 5288, 2583, 5281, 0)
val WAVE_LOBBY6: ZoneBorders = ZoneBorders(2584, 5288, 2591, 5281, 0)
val WAVE_LOBBY7: ZoneBorders = ZoneBorders(2595, 5288, 2602, 5281, 0)
val WAVE_LOBBY8: ZoneBorders = ZoneBorders(2603, 5288, 2610, 5281, 0)
val WAVE_LOBBY9: ZoneBorders = ZoneBorders(2576, 5278, 2583, 5271, 0)
val WAVE_LOBBY10: ZoneBorders = ZoneBorders(2584, 5278, 2591, 5271, 0)
private val REGION_10322 = getRegionBorders(10322)
val areaBorders = arrayOf(
QUICK_START,
WAVE_LOBBY1,
WAVE_LOBBY2,
WAVE_LOBBY3,
WAVE_LOBBY4,
WAVE_LOBBY5,
WAVE_LOBBY6,
WAVE_LOBBY7,
WAVE_LOBBY8,
WAVE_LOBBY9,
WAVE_LOBBY10,
REGION_10322
)
val waveLobbies = listOf(
MAIN_LOBBY,WAVE_LOBBY1, WAVE_LOBBY2, WAVE_LOBBY3, WAVE_LOBBY4,
WAVE_LOBBY5, WAVE_LOBBY6, WAVE_LOBBY7, WAVE_LOBBY8,
WAVE_LOBBY9, WAVE_LOBBY10
)
val BARBDOORS = intArrayOf(
Scenery.DOOR_20199,
Scenery.DOOR_20200,
Scenery.DOOR_20201,
Scenery.DOOR_20202,
Scenery.DOOR_20203,
Scenery.DOOR_20204,
Scenery.DOOR_20205,
Scenery.DOOR_20206,
Scenery.DOOR_20207,
Scenery.DOOR_20208,
)
}
val BARBWAVEDOOR = hashMapOf(
20199 to 1,
20200 to 2,
20201 to 3,
20202 to 4,
20203 to 5,
20204 to 6,
20205 to 7,
20206 to 8,
20207 to 9,
20208 to 10,
20209 to 1)
}
/*
PRIVATE_PALDO_5031 -- Big axe barb at entrance to main lobby
PRIVATE_PALDON_5034 -- sword -- Above wave 7 and 8 Source:https://youtu.be/VPlIYB1h9KM?t=340
PRIVATE_PENDRON_5032 -- Mace -- Above arrow Guessing
PRIVATE_PIERREB_5033 -- bowman -- Above wave 9 and 10 Source:https://youtu.be/XHp7g7dz12M?t=83
DOOR_20209
[Title][red] "Which role would you like to perform?"
[0] "Attacker"
[1] "Defender"
[2] "Collector"
[3] "Healer"
[4] "I'll be any role!"
SCROLL_10512 -- LeaderScroll
QUEEN_HELP_BOOK_10562 -- Get from cain and POH bookcase
1889 5407 - Barb assualt QUEEN area
1891 5465 - barb assualt AREA under
*/

View file

@ -0,0 +1,212 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.lobby.BarbTeamManager.createTeam
import content.minigame.barbassault.setBASession
import core.api.*
import core.game.activity.ActivityManager
import core.game.activity.ActivityPlugin
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.GameWorld
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import core.plugin.Initializable
import core.tools.Log
import org.rs09.consts.Items
private var activity: BarbassQuickWaveActivity? = null
data class WaitingPlayer(val player: Player, val role: BarbRole?,val joinTime: Long = System.currentTimeMillis())
private val waitingPlayers = mutableListOf<WaitingPlayer>()
private val waitingRoleCounts = mutableMapOf<BarbRole, Int>().withDefault { 0 }
private var waitingAnyCount = 0
@Initializable
open class BarbassQuickWaveActivity : ActivityPlugin("BarbassQuickWave",false, false, true,
ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.FOLLOWERS, ZoneRestriction.RANDOM_EVENTS), MapArea {
init { activity = this; this.safeRespawn = Location.create(2593, 5264, 0) }
private fun tryCreateTeamFromQueue(): Boolean {
if (waitingPlayers.size < 5) return false
val remaining = waitingPlayers.sortedBy { it.joinTime }.toMutableList()
val selected = mutableListOf<WaitingPlayer>()
fun takeForRole(role: BarbRole): WaitingPlayer? {
val exact = remaining.firstOrNull { it.role == role }
if (exact != null) {
remaining.remove(exact)
return exact
}
val any = remaining.firstOrNull { it.role == null }
if (any != null) {
remaining.remove(any)
return any.copy(role = role)
}
return null
}
for (role in BarbRole.values()) {
val picked = takeForRole(role) ?: return false
selected.add(picked)
}
val fifth = remaining.firstOrNull() ?: return false
remaining.remove(fifth)
val assignedFifth = if (fifth.role == null) { fifth.copy(role = BarbRole.ATTACKER) } else fifth
selected.add(assignedFifth)
createQuickTeam(selected)
return true
}
private fun giveScroll(player: Player): Boolean {
if (!player.inventory.containsItem(Item(Items.SCROLL_10512, 1))) {
return player.inventory.add(Item(Items.SCROLL_10512, 1))
}
return true
}
//todo Make Game Start Faster once a team is created.
private fun createQuickTeam(players: List<WaitingPlayer>) {
// if(player == null) {removeQuickQue(player);return}
var teamLeader: Player? = null
for (wp in players) {
if(wp.player.isArtificial) continue //only players can lead a party.
if (giveScroll(wp.player)) {
teamLeader = wp.player
break
}
}
//IF BY CHANCE no one had free inventory space KICK THEM ALL FROM QuickLobby!
// I should just return them to the que, at the end instead
if (teamLeader == null) {
players.forEach {
sendMessage(it.player, "You could not hold a Scroll and have been removed from queue")
it.player.teleport(Location.create(2602, 5279, 0))
}
return
}
if (createTeam(teamLeader)){
players.forEach { removeQuickQue(it.player) }
val session = getBASession(teamLeader)
val team = session?.team ?: return
players.forEachIndexed { index, wrapper ->
wrapper.let { playerWrapper ->
if (wrapper.player != teamLeader) {
setBASession(playerWrapper.player,session)
team.addPlayer(playerWrapper.player)
}
playerWrapper.role?.let { role ->
team.setRoleForPlayer(playerWrapper.player, role)
}
}
}
session.beginStartingPhase(true)
}
}
override fun configure() {
GameWorld.Pulser.submit(
object : Pulse(25) {
override fun pulse(): Boolean {
if (waitingPlayers.isNotEmpty()) {
tryCreateTeamFromQueue()
}
return false
}
})
}
override fun death(e: Entity?, killer: Entity?): Boolean {
val ename = e?.name
log(this.javaClass, Log.FINE,"$ename has died in Barbass Lobby, HOW? killer:${killer?.name}")
e!!.getProperties().setTeleportLocation(null)
return true
}
override fun newInstance(p: Player?): ActivityPlugin {
ActivityManager.register(this)
return this
}
//override when !2265 passes (void recovery)
fun recover(player: Player){
//do nothing, not lost
}
override fun getSpawnLocation(): Location {
return Location.create(2593, 5264, 0)
}
companion object {
fun removeQuickQue(player: Player){
val toRemove = waitingPlayers.filter { it.player == player }
waitingPlayers.removeAll(toRemove)
toRemove.forEach { wp ->
if (wp.role == null) waitingAnyCount--
else waitingRoleCounts[wp.role] = waitingRoleCounts.getValue(wp.role) - 1
}
refreshQuickWaveOverlay()
}
fun addToQueue(player: Player, role: BarbRole?) {
waitingPlayers.add(WaitingPlayer(player, role))
if (role == null) waitingAnyCount++
else waitingRoleCounts[role] = waitingRoleCounts.getValue(role) + 1
//todo find authentic message for Quick Start Wave BarbAssault, If there even is one.
val roleText = role?.name ?: "ANY"
sendMessage(player, "You've joined the queue as ${roleText.lowercase().replaceFirstChar { it.uppercase() }}.")
refreshQuickWaveOverlay()
}
private fun refreshQuickWaveOverlay() {
waitingPlayers.forEach {
val itplayer = it.player
setInterfaceText(itplayer, (waitingRoleCounts[BarbRole.ATTACKER] ?: 0).toString(), 489, 6)
setInterfaceText(itplayer, (waitingRoleCounts[BarbRole.DEFENDER] ?: 0).toString(), 489, 7)
setInterfaceText(itplayer, (waitingRoleCounts[BarbRole.COLLECTOR] ?: 0).toString(), 489, 8)
setInterfaceText(itplayer, (waitingRoleCounts[BarbRole.HEALER] ?: 0).toString(), 489, 9)
setInterfaceText(itplayer, waitingAnyCount.toString(), 489, 10)
}
}
}
/* override fun areaEnter(entity: Entity) {
enter(entity)
}
override fun areaLeave(entity: Entity, logout: Boolean) {
leave(entity,logout)
super.areaLeave(entity, logout)
}
override fun enter(e: Entity?): Boolean {
return super.enter(e)
}
override fun leave(e: Entity?, logout: Boolean): Boolean {
return super.leave(e, logout)
}*/
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(getRegionBorders(10322))
}
}

View file

@ -0,0 +1,169 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.BAActivity
import content.minigame.barbassault.arena.BarbassaultSession
import content.minigame.barbassault.arena.BarbassaultState
import content.minigame.barbassault.getBALevels
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.getBAWave
import content.minigame.barbassault.setBASession
import core.api.sendMessage
import core.game.node.entity.player.Player
import core.game.world.map.Location
data class PartySlot( val index: Int, val player: Player, var role: BarbRole? = null )
data class PendingRecruit( var player: Player, var role: BarbRole? = null,var accept:Boolean = false ) {
fun roleLevel(): Int = when (role) {
BarbRole.ATTACKER -> getBALevels(player).atk
BarbRole.COLLECTOR -> getBALevels(player).col
BarbRole.DEFENDER -> getBALevels(player).def
BarbRole.HEALER -> getBALevels(player).heal
null -> 1
}
}
private val PartySpawns = arrayOf(30 to 18, 29 to 17, 31 to 17, 28 to 16, 32 to 16)
class BarbTeam(val session: BarbassaultSession, leader: Player) {
companion object {
const val MAX_SLOTS = 5
const val LEADER_SLOT = 0
}
var wave = 1
var recruit: PendingRecruit? = null
private val slots: Array<PartySlot?> = arrayOfNulls(MAX_SLOTS)
init { slots[LEADER_SLOT] = PartySlot(LEADER_SLOT, leader); wave = getBAWave(leader)!! }
fun slotOf(player: Player): Int = slots.indexOfFirst { it?.player == player }
fun getSlot(index: Int): PartySlot? = slots.getOrNull(index)
fun allPlayers(): List<Player> = slots.filterNotNull().map { it.player }
fun isLeader(player: Player): Boolean = slotOf(player) == LEADER_SLOT
fun isHealer(player: Player): Boolean = getRoleForPlayer(player) == BarbRole.HEALER
fun isFull(): Boolean = slots.all { it != null }
fun addPlayer(player: Player): Boolean {
if (slotOf(player) != -1) return false
val freeIndex = slots.indexOfFirst { it == null }
if (freeIndex == -1) return false
slots[freeIndex] = PartySlot(freeIndex, player)
return true
}
fun removePlayer(player: Player) {
val idx = slotOf(player)
if (idx == -1) return
slots[idx] = null
}
fun setRoleForPlayer(player: Player, role: BarbRole): Boolean {
val idx = slotOf(player)
if (idx == -1) return false
slots[idx]?.role = role
allPlayers().forEach { updateRoleAvailabilityFor(it) }
return true
}
fun getPlayersByRole(barbRole: BarbRole): List<Player> = slots.filterNotNull().map { it.player }.filter { getRoleForPlayer(it) == barbRole }
fun getRoleForPlayer(player: Player): BarbRole = slots.getOrNull(slotOf(player))?.role!!
fun getLeader(): PartySlot? = slots[0]
fun roleCounts(): RoleCounts {
var counts = RoleCounts(0, 0, 0, 0)
slots.forEach { slot ->
when (slot?.role) {
BarbRole.ATTACKER -> counts.att++
BarbRole.DEFENDER -> counts.def++
BarbRole.COLLECTOR -> counts.coll++
BarbRole.HEALER -> counts.heal++
null -> null
}
}
return RoleCounts(counts.att, counts.def, counts.coll, counts.heal)
}
fun updateRoleAvailabilityFor(player: Player) = updateRoleAvailability(roleCounts(),assignedRoleCount(),player )
fun assignedRoleCount(): Int = slots.count { it?.role != null }
fun spawnLocationFor(player: Player): Location {
val (x, y) = PartySpawns[slotOf(player)]
return session.base.transform(x, y, 0)
}
}
data class RoleCounts(var att: Int, var def: Int, var coll: Int, var heal: Int )
object BarbTeamManager {
fun createTeam(leader: Player): Boolean {
val session = BAActivity.createSession(leader)
val team = BarbTeam(session, leader)
session.team = team
leader.debug("[DEBUG] TeamCreated")
updateTeam(team)
return true
}
fun disbandTeam(session: BarbassaultSession) {
val team = session.team
team.allPlayers().forEach {
it.sendMessage("Your leader has left the room and therefore cleared the team!")
it.removeAttribute("ba-session")
updateCurrentTeam(it)
//todo Do not forget to remove this latter.
if (session.state == BarbassaultState.STARTING) {
it.properties.teleportLocation = Location.create(2593, 5264, 0)
}
}
session.endSession()
}
fun joinTeam(player: Player, role: BarbRole): Boolean {
val session = getBASession(player) ?: return false
val team = session.team
team.recruit = null
if (!team.addPlayer(player)) {
player.debug("[DEBUG] That party is full.")
return false
}
team.setRoleForPlayer(player, role)
team.allPlayers().forEach {
updateCurrentTeam(it)
if (it != player)
it.sendMessage("Your application has been accepted")
}
return true
}
fun abandonTeam(player: Player) {
val session = getBASession(player) ?: return
val team = session.team
if (team.isLeader(player)) {
disbandTeam(session)
return
}
team.removePlayer(player)
setBASession(player,null)
updateCurrentTeam(player)
updateTeam(team)
//session.cancleStartingPhase()
}
fun updateTeam(team: BarbTeam) = team.allPlayers().forEach { updateCurrentTeam(it) }
}
//Leader leaving room and causing disband "Your leader has left the room and therefore cleared the team!"
// //"Your recruiter exited the room."
//Accpting invite text " Your application has been accepted"
//The applicant declined your offer.
//the other player hasn't confirmed their role yet.
//the applicant has chosen a role.0
//You removed the person from the team.

View file

@ -0,0 +1,73 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.lobby.CurrentTeam.ROLE_MODEL_MAP
import content.minigame.barbassault.lobby.CurrentTeam.modelZoom
import core.api.setInterfaceModel
import core.api.setInterfaceText
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Items
@Initializable
//not used delete this plugin latter
class CurrentTeamInterface : ComponentPlugin() {
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(CurrentTeam.ID).plugin = this
return this
}
override fun handle(player: Player?,component: Component?,opcode: Int,button: Int,slot: Int,itemId: Int ): Boolean {
return true
}
override fun open(player: Player?, component: Component?) {
super.open(player, component)
player ?: return
if(player.inventory.containsItem(Item(Items.SCROLL_10512, 1)) ) {
// setInterfaceText(player, player.username, CurrentTeam.ID, CurrentTeam.team[0].name)
//we dont become leaders if we own a scroll , we become one when we write our name on scroll.
}
}
}
fun updateCurrentTeam(player: Player) {
val team = getBASession(player)?.team
for (slotIndex in 0 until BarbTeam.MAX_SLOTS) {
val uiSlot = CurrentTeam.team[slotIndex]
val partySlot = team?.getSlot(slotIndex)
val slotPlayer = partySlot?.player
val slotRole = partySlot?.role
val nameText = slotPlayer?.username?.capitalizeWords() ?: CurrentTeam.defaultText
val model = slotRole?.let { ROLE_MODEL_MAP[it] } ?: -1
setInterfaceText(player, nameText, CurrentTeam.ID, uiSlot.name)
setInterfaceModel(player, model, CurrentTeam.ID, uiSlot.role, modelZoom)
}
}
fun String.capitalizeWords(): String = split('_').joinToString("_") { it.replaceFirstChar { c -> c.uppercaseChar() } }
object CurrentTeam {
const val ID = 256
val defaultText = "-----"
val modelZoom = 2372
val ROLE_MODEL_MAP = mapOf(BarbRole.ATTACKER to 20561,BarbRole.COLLECTOR to 20563,BarbRole.DEFENDER to 20566,BarbRole.HEALER to 20569)
data class Slot(val role: Int, val name: Int)
val team = listOf(
Slot(16, 6),
Slot(17, 7),
Slot(18, 8),
Slot(19, 9),
Slot(20, 10)
)
}

View file

@ -0,0 +1,85 @@
package content.minigame.barbassault
import core.api.animate
import core.api.sendMessage
import core.game.dialogue.DialoguePlugin
import core.game.dialogue.FacialExpression
import core.game.node.entity.npc.NPC
import core.game.node.entity.player.Player
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import core.tools.END_DIALOGUE
import org.rs09.consts.NPCs
@Initializable
class PrivatesDialogues(player: Player? = null) : DialoguePlugin(player){
val NPCtoDialogStage = mapOf(
NPCs.PRIVATE_PALDON_5034 to 1,
NPCs.PRIVATE_PALDO_5031 to 6,
NPCs.PRIVATE_PIERREB_5033 to 11,
NPCs.PRIVATE_PENDRON_5032 to 20,
)
val PRIVATES: IntArray = NPCtoDialogStage.keys.toIntArray()
override fun newInstance(player: Player?): DialoguePlugin {
return PrivatesDialogues(player)
}
override fun open(vararg args: Any?): Boolean {
val currentNPC = args[0] as NPC
npc = currentNPC
stage = NPCtoDialogStage[currentNPC.id] ?: -1
sendMessage(player, stage.toString())
handle(0,0)
return true
}
override fun handle(componentID: Int, buttonID: Int): Boolean {
when (stage) {
//PRIVATE_PALDON
1 -> playerl(FacialExpression.FRIENDLY, "Hi.").also { stage++ }
2 -> npcl(FacialExpression.ANNOYED, "Shhh. Don't talk to me.").also { stage++ }
3 -> playerl(FacialExpression.THINKING, "What? Why?").also { stage++ }
4 -> npcl(FacialExpression.AFRAID, "If I'm seen slacking off by talking to you, I'll be in deep trouble!").also { stage++ }
5 -> playerl(FacialExpression.WORRIED, "Oh... Sorry.").also { stage = END_DIALOGUE}
//PRIVATE_PALDO
6 -> playerl(FacialExpression.FRIENDLY, "Hi, soldier..").also { stage++ }
7 -> npcl(FacialExpression.FRIENDLY, "Why, hello there!").also { stage++ }
8 -> playerl(FacialExpression.THINKING, "You seem like a jolly chap.").also { stage++ }
9 -> npcl(FacialExpression.FRIENDLY, "And why not? 'Tis a greeeaat day!").also { stage++ }
10 -> playerl(FacialExpression.FRIENDLY, "Well, I suppose it is.").also { stage = END_DIALOGUE }
//PRIVATE_PIERREB
11 -> playerl(FacialExpression.ASKING, "Hello. So you're just a private?").also { stage++ }
12 -> npcl(FacialExpression.ANGRY, "Show some respect! It's more than you'll achieve.").also { stage++}
13 -> playerl(FacialExpression.ANNOYED, "I beg to differ. I'm in perfect shape!").also { stage++ }
14 -> npcl(FacialExpression.ANGRY, "Prove it!").also { stage++}
15 -> playerl(FacialExpression.ASKING, "How?").also { stage++ }
16 -> npcl(FacialExpression.ANGRY, "Give me five star-jumps!").also {
animate(player, Animation(2761))
stage++
}
17 -> npcl(FacialExpression.ANGRY, "Five sit-ups.").also {
animate(player, Animation(2763))
stage++
}
18 -> npcl(FacialExpression.ANGRY, "Run on the spot!").also {
animate(player, Animation(2764))
stage++
}
19 -> npcl(FacialExpression.THINKING, "Okay. Maybe you have what it takes. Best you speak with the captain.").also { stage = END_DIALOGUE }
//PRIVATE_PENDRON
20 -> playerl(FacialExpression.FRIENDLY, "Hi there.").also { stage++ }
21 -> npcl(FacialExpression.ASKING, "Don't suppose you've seen a battleaxe around here?").also { stage++ }
22 -> playerl(FacialExpression.FRIENDLY, "A battleaxe? Nope, afraid not.").also { stage++ }
23 -> npcl(FacialExpression.WORRIED, "the captain is going to kill me if he finds out I've lost my weapon.").also { stage = END_DIALOGUE }
}
return true
}
override fun getIds(): IntArray {
return PRIVATES
}
}

View file

@ -0,0 +1,163 @@
package content.minigame.barbassault.lobby
import content.minigame.barbassault.BarbRole
import content.minigame.barbassault.getBASession
import content.minigame.barbassault.setBASession
import core.api.*
import core.game.component.Component
import core.game.component.ComponentDefinition
import core.game.component.ComponentPlugin
import core.game.node.entity.player.Player
import core.plugin.Initializable
import core.plugin.Plugin
@Initializable
class RoleSelectInterface : ComponentPlugin() {
override fun open(player: Player?, component: Component?) {
super.open(player, component)
var isopen = true
player ?: return
val team = getBASession(player)?.team;
if (team != null) { team.updateRoleAvailabilityFor(player) }
component?.setCloseEvent { player, c ->
val leader = team?.getLeader()?.player;
if (leader != null && leader != player && isopen) {
isopen = false
closeInterface(leader)
//if team.recruit not on team, team.recruit?.player?.baSession = null
(team.recruit?.player )?.let { setBASession(it ,null) }
team.recruit = null
}
return@setCloseEvent true
}
val leaderData = getBASession(player)?.team?.getLeader() ?: run {
player.sendMessage("No leader found for your team.")
return
}
setInterfaceText(player, leaderData!!.player.name.capitalizeWords(),SelectRole.ID,SelectRole.Leader.name)
setInterfaceText(player, leaderData.role.toString(),SelectRole.ID,SelectRole.Leader.role)
setInterfaceText(player,"0",SelectRole.ID,SelectRole.Leader.wave)
}
var selectedButton = -1
val roleButtons = mapOf( SelectRole.Att to listOf(8, 9), SelectRole.Def to listOf(10, 11), SelectRole.Col to listOf(12, 13),SelectRole.Heal to listOf(14,15) )
val buttonRoleMap = roleButtons.flatMap { (role, buttons) -> buttons.map { it to role }}.toMap()
val selectRoleToBarbRole = mapOf(SelectRole.Att to BarbRole.ATTACKER,SelectRole.Def to BarbRole.DEFENDER, SelectRole.Col to BarbRole.COLLECTOR, SelectRole.Heal to BarbRole.HEALER )
override fun handle(player: Player?, component: Component?, opcode: Int, button: Int, slot: Int, itemId: Int): Boolean {
if (player == null) return true
val team = getBASession(player)?.team;
val pending = team?.recruit ?: return true
var waitingSelected: BarbRole? = null
when (button) {
SelectRole.AcceptButton, SelectRole.AcceptButtonText -> {
val selectedRole = pending.role
val selectedRoleName = selectedRole?.name?.lowercase()?.replaceFirstChar { it.uppercase() }
if (selectedRole != null) {
waitingSelected = selectedRole
AcceptRoleInterface.updateRecruitData(player)
if (!pending.accept) {team.getLeader()?.player?.sendMessages("The applicant has chosen $selectedRoleName")}
pending.accept = true
} else {
player.sendMessage("You must select a role first!")
}
return true
}
SelectRole.DeclineButton, SelectRole.DeclineButtonText -> {
handleDecline(player)
return true
}
}
val clickedSelectRole = buttonRoleMap[button]
if (clickedSelectRole != null && clickedSelectRole.isActive) {
pending.role = selectRoleToBarbRole[clickedSelectRole]
if (pending.role != waitingSelected ) {
pending.accept = false
AcceptRoleInterface.resetWaitingStatus(player)
}
val displayName = pending.role?.name?.capitalizeWords() ?: "Unknown"
setInterfaceText(player, displayName, SelectRole.ID, SelectRole.Status.CurrentRole)
selectedButton = button
} else {
}
return true
}
override fun newInstance(arg: Any?): Plugin<Any> {
ComponentDefinition.forId(SelectRole.ID).plugin = this
return this
}
}
fun updateRoleAvailability( counts: RoleCounts, partySize: Int, player: Player ) {
fun canAdd(current: Int, missingRoles: Int): Boolean {
if (partySize >= 5) return false
if (current >= 2) return false
val remainingSlots = 5 - (partySize + 1)
return remainingSlots >= missingRoles
}
val missingRoles = listOf(counts.att, counts.def, counts.coll, counts.heal ).count { it == 0 }
setRoleActive( SelectRole.Att, canAdd( counts.att, if ( counts.att == 0) maxOf(0, missingRoles - 1) else missingRoles ), player )
setRoleActive( SelectRole.Def, canAdd( counts.def, if ( counts.def == 0) maxOf(0, missingRoles - 1) else missingRoles ), player )
setRoleActive( SelectRole.Col, canAdd( counts.coll,if (counts.coll == 0) maxOf(0, missingRoles - 1) else missingRoles ), player )
setRoleActive(SelectRole.Heal, canAdd( counts.heal,if (counts.heal == 0) maxOf(0, missingRoles - 1) else missingRoles ), player )
}
private fun setRoleActive(role: SelectRole.Role, active: Boolean, player: Player) {
role.setActive(active)
setInterfaceModel(player,role.model,SelectRole.ID,role.componentId,0)
setComponentVisibility(player,SelectRole.ID,role.activeChild,!role.isActive)
}
private fun handleAccept(player: Player){
return
}
private fun handleDecline(player: Player) {
val leader = getBASession(player)?.team?.getLeader()?.player
sendMessage(leader!!, "The applicant declined your offer.")
closeInterface(player)
}
object SelectRole {
const val ID = 493
const val AcceptButton = 16
const val AcceptButtonText = 17
const val DeclineButton = 18
const val DeclineButtonText = 7
class Role(val roleName: String, val componentId: Int, val activeModel: Int, val inactiveModel: Int, val activeChild: Int) {
var isActive: Boolean = false; private set
fun setActive(active: Boolean) { isActive = active }
val model: Int get() = if (isActive) activeModel else inactiveModel
}
val Att = Role("Attacker",8, 20561, 20560, 20)
val Def = Role("Defender",10, 20566, 20567, 55)
val Col = Role("Collector",13, 20563, 20564, 54)
val Heal = Role("Healer", 14, 20569, 20570, 53)
data class Slot(val name: Int, val role: Int, val wave: Int)
val Leader = Slot(28, 33, 39)
val Followers = listOf(
Slot(29, 34, 40),
Slot(30, 35, 41),
Slot(31, 36, 42),
Slot(32, 37, 43),
)
object Status {
const val CurrentRole = 19
const val WaitingForAccept = 22
}
}

View file

@ -65,7 +65,16 @@ class FOGRewardsInterface : ComponentPlugin(){
return this
}
private fun handleOpcode(item: ShopItem, opcode: Int, player: Player){
private fun handleOpcode(item: ShopItem, opcode: Int, player: Player) {
if (item.id in shopsaleone) {
handleSaleOneOpcode(item,opcode, player)
} else {
handleOGOpcode(item,opcode, player)
}
}
private fun handleOGOpcode(item: ShopItem, opcode: Int, player: Player){
when(opcode){
155 -> player.sendMessage("${ItemDefinition.forId(item.id).name.replace("100","")}: costs ${item.price} tokens.")
196 -> handleBuyOption(item,1,player)
@ -75,6 +84,14 @@ class FOGRewardsInterface : ComponentPlugin(){
}
}
private fun handleSaleOneOpcode(item: ShopItem, opcode: Int, player: Player) {
when (opcode) {
155 -> player.sendMessage("${ItemDefinition.forId(item.id).name.replace("100","")}: costs ${item.price} tokens.")
196 -> handleBuyOption(item,1,player)
124 -> player.sendMessage(ItemDefinition.forId(item.id).examine.replace("100",""))
}
}
private fun handleBuyOption(item: ShopItem, amount: Int, player: Player){
val neededTokens = Item(12852,item.price * amount)
if(player.inventory.containsItem(neededTokens)){
@ -88,39 +105,41 @@ class FOGRewardsInterface : ComponentPlugin(){
}
val Druidic_Mage_Top = (ShopItem(12894, 300, 1))
val Druidic_Mage_Hood = (ShopItem(12887, 100, 1))
val Druidic_Mage_Bottom = (ShopItem(12901, 200, 1))
val Combat_Robe_Top = (ShopItem(12971, 150, 1))
val Combat_Robe_Hood = (ShopItem(12964, 50, 1))
val Combat_Robe_Bottom = (ShopItem(12978, 100, 1))
val Battle_Robe_Hood = (ShopItem(12866, 250, 1))
val Battle_Robe_Top = (ShopItem(12873, 1500, 1))
val Battle_Robe_Bottom = (ShopItem(12880, 1000, 1))
val Green_Coif = (ShopItem(12936, 150, 1))
val Blue_Coif = (ShopItem(12943, 200, 1))
val Red_Coif = (ShopItem(12950, 300, 1))
val Black_Coif = (ShopItem(12957, 500, 1))
val Bronze_Gaunt = (ShopItem(12985, 15, 1))
val Iron_Gaunt = (ShopItem(12988, 30, 1))
val Steel_Gaunt = (ShopItem(12991, 50, 1))
val Black_Gaunt = (ShopItem(12994, 75, 1))
val Mithril_Gaunt = (ShopItem(12997, 100, 1))
val Adamant_Gaunt = (ShopItem(13000, 150, 1))
val Rune_Gaunt = (ShopItem(13003, 200, 1))
val Dragon_Gaunt = (ShopItem(13006, 300, 1))
val Addy_Spike = (ShopItem(12908, 50, 1))
val Addy_Beserk = (ShopItem(12915, 100, 1))
val Rune_Spike = (ShopItem(12922, 200, 1))
val Rune_Beserk = (ShopItem(12929, 300, 1))
val Air_Gloves = (ShopItem(12863, 75, 1))
val Water_Gloves = (ShopItem(12864, 75, 1))
val Earth_Gloves = (ShopItem(12865, 75, 1))
val Irit_Gloves = (ShopItem(12856, 75, 1))
val Avantoe_Gloves = (ShopItem(12857, 100, 1))
val Kwuarm_Gloves = (ShopItem(12858, 200, 1))
val Cadantine_Gloves = (ShopItem(12859, 200, 1))
val Swordfish_Gloves = (ShopItem(12860, 200, 1))
val Shark_Gloves = (ShopItem(12861, 200, 1))
val Dragon_Gloves = (ShopItem(12862, 200, 1))
val Druidic_Mage_Top = (ShopItem(12894, 300, 1))
val Druidic_Mage_Hood = (ShopItem(12887, 100, 1))
val Druidic_Mage_Bottom = (ShopItem(12901, 200, 1))
val Combat_Robe_Top = (ShopItem(12971, 150, 1))
val Combat_Robe_Hood = (ShopItem(12964, 50, 1))
val Combat_Robe_Bottom = (ShopItem(12978, 100, 1))
val Battle_Robe_Hood = (ShopItem(12866, 250, 1))
val Battle_Robe_Top = (ShopItem(12873, 1500, 1))
val Battle_Robe_Bottom = (ShopItem(12880, 1000, 1))
val Green_Coif = (ShopItem(12936, 150, 1))
val Blue_Coif = (ShopItem(12943, 200, 1))
val Red_Coif = (ShopItem(12950, 300, 1))
val Black_Coif = (ShopItem(12957, 500, 1))
val Bronze_Gaunt = (ShopItem(12985, 15, 1))
val Iron_Gaunt = (ShopItem(12988, 30, 1))
val Steel_Gaunt = (ShopItem(12991, 50, 1))
val Black_Gaunt = (ShopItem(12994, 75, 1))
val Mithril_Gaunt = (ShopItem(12997, 100, 1))
val Adamant_Gaunt = (ShopItem(13000, 150, 1))
val Rune_Gaunt = (ShopItem(13003, 200, 1))
val Dragon_Gaunt = (ShopItem(13006, 300, 1))
val Addy_Spike = (ShopItem(12908, 50, 1))
val Addy_Beserk = (ShopItem(12915, 100, 1))
val Rune_Spike = (ShopItem(12922, 200, 1))
val Rune_Beserk = (ShopItem(12929, 300, 1))
val Air_Gloves = (ShopItem(12863, 75, 1))
val Water_Gloves = (ShopItem(12864, 75, 1))
val Earth_Gloves = (ShopItem(12865, 75, 1))
val Irit_Gloves = (ShopItem(12856, 75, 1))
val Avantoe_Gloves = (ShopItem(12857, 100, 1))
val Kwuarm_Gloves = (ShopItem(12858, 200, 1))
val Cadantine_Gloves = (ShopItem(12859, 200, 1))
val Swordfish_Gloves = (ShopItem(12860, 200, 1))
val Shark_Gloves = (ShopItem(12861, 200, 1))
val Dragon_Gloves = (ShopItem(12862, 200, 1))
val shopsaleone = setOf(12863,12864,12865,12856,12857,12858,12859,12860,12861,12862)
}

View file

@ -70,8 +70,8 @@ public class CrateCutscenePlugin extends CutscenePlugin {
@Override
public Location getStartLocation() {
return base.transform(18, 25, 0);
}
return base.transform(26, 47, 0);
}// old 18 25
@Override
public Location getSpawnLocation() {
@ -80,13 +80,13 @@ public class CrateCutscenePlugin extends CutscenePlugin {
@Override
public void configure() {
region = DynamicRegion.create(12609);
region = DynamicRegion.create(11161);
setRegionBase();
registerRegion(region.getId());
SceneryBuilder.add(new Scenery(65, base.transform(18, 25, 0), 0, 0));
SceneryBuilder.add(new Scenery(65, base.transform(19, 25, 0), 0, 4));
SceneryBuilder.add(new Scenery(65, base.transform(18, 24, 0), 0, 1));
SceneryBuilder.add(new Scenery(65, base.transform(18, 26, 0), 0, 3));
//SceneryBuilder.add(new Scenery(65, base.transform(18, 25, 0), 0, 0));
//SceneryBuilder.add(new Scenery(65, base.transform(19, 25, 0), 0, 4));
//SceneryBuilder.add(new Scenery(65, base.transform(18, 24, 0), 0, 1));
//SceneryBuilder.add(new Scenery(65, base.transform(18, 26, 0), 0, 3));
}
/**

View file

@ -67,6 +67,7 @@ import core.game.world.map.Direction
import core.game.world.map.Location
import core.game.world.map.RegionManager
import core.game.world.map.RegionManager.getRegionChunk
import core.game.world.map.path.ClipMaskSupplier
import core.game.world.map.path.Pathfinder
import core.game.world.map.zone.MapZone
import core.game.world.map.zone.ZoneBorders
@ -1459,7 +1460,8 @@ fun reinitVarps (player: Player) {
*/
fun forceWalk(entity: Entity, dest: Location, type: String) {
if (type == "clip") {
ForceMovement(entity, dest, 10, 10).run()
val NO_CLIP = ClipMaskSupplier { _, _, _ -> 0 }
Pathfinder.find(entity.location,entity.size(),dest,true,Pathfinder.SMART,NO_CLIP ).walk(entity)
return
}
val pathfinder = when (type) {
@ -1758,6 +1760,23 @@ fun setInterfaceText(player: Player, string: String, iface: Int, child: Int) {
player.packetDispatch.sendString(string, iface, child)
}
/**
* Sends a model to a specific interface child
* @param player the player to send the packet to
* @param modelId the modelId to send to the child
* @param iface the ID of the interface to use
* @param child the index of the child
* @param zoom the zoom of the model
* @param pitch optional pitch angle
* @param yaw optional yaw angle
*/
fun setInterfaceModel( player: Player, modelId: Int, iface: Int, child: Int, zoom: Int, pitch: Int? = null, yaw: Int? = null) {
player.packetDispatch.sendModelOnInterface(modelId, iface, child, zoom)
if (pitch != null && yaw != null) {
player.packetDispatch.sendAngleOnInterface(iface, child, zoom, pitch, yaw)
}
}
/**
* Allows you to hide or show specific children in an interface
* @param player the player to send the packet to

View file

@ -102,7 +102,7 @@ class GeneralBotCreator {
return false
val idleRoll = RandomFunction.random(10)
if(idleRoll == 2 && botScript !is Idler){
if(idleRoll == 2 && botScript !is Idler && !botScript.preventRandomIdle){
randomDelay += RandomFunction.random(20,50)
return false
}

View file

@ -22,6 +22,8 @@ public abstract class Script {
public boolean running = true;
public boolean endDialogue = true;
public boolean preventRandomIdle = false;
public void init(boolean isPlayer)
{
//bot.init();

View file

@ -104,7 +104,8 @@ class ScriptAPI(private val bot: Player) {
val item = bot.inventory.getItem(Item(itemId))
val childNode = node.asScenery()?.getChild(bot)
//val childNode = node.asScenery()?.getChild(bot)
val childNode = if (node is Scenery) node.getChild(bot) else null
if (InteractionListeners.run(item, node, type, bot))
return

View file

@ -1,6 +1,7 @@
package core.game.node.entity.combat;
import content.data.EnchantedJewellery;
import content.minigame.barbassault.arena.BarbassaultSession;
import core.game.container.impl.EquipmentContainer;
import core.game.node.entity.skill.Skills;
import content.global.skill.summoning.familiar.Familiar;
@ -203,6 +204,14 @@ public final class ImpactHandler {
if (p.getAttribute("godMode", false)) {
p.getSkills().heal(10000);
}
if (hit > 0) {
BarbassaultSession session = p.getAttribute("ba-session");
if (session != null) {
if (session.isInBarbAssaultGame()) {
session.updateBarbHealerIfaceHPOfPlayer(p);
}
}
}
}
Impact impact = new Impact(source, hit, style, type);
impactQueue.add(impact);

View file

@ -9,6 +9,7 @@ import java.util.Map;
import java.util.Objects;
import static core.api.ContentAPIKt.setVarbit;
import static core.api.ContentAPIKt.setVarp;
/**
* Represents a managing class of a players spell book.
@ -45,6 +46,8 @@ public final class SpellBookManager {
public void update(Player player) {
player.getInterfaceManager().openTab(new Component(spellBook));
setVarbit(player, 357, Objects.requireNonNull(SpellBook.forInterface(spellBook)).ordinal());
// Allows Catalytic and Elemental runes to be show as useable runes.
setVarp(player,1331, 1);
}
/**

View file

@ -1,12 +1,11 @@
package core.game.node.entity.player.link.prayer;
import content.data.Quests;
import core.game.node.entity.player.link.diary.DiaryType;
import content.minigame.barbassault.arena.BarbassaultSession;
import core.game.node.entity.skill.SkillBonus;
import core.game.node.entity.skill.Skills;
import core.game.node.entity.player.Player;
import core.game.node.entity.player.link.audio.Audio;
import core.game.world.map.zone.ZoneBorders;
import core.tools.StringUtils;
import core.game.event.*;
import org.rs09.consts.Sounds;
@ -222,6 +221,14 @@ public enum PrayerType {
* @return <code>True</code> if it is permitted.
*/
public boolean permitted(final Player player) {
BarbassaultSession session = player.getAttribute("ba-session");
if (session != null) {
if (session.isInBarbAssaultGame()) {
sendMessage(player, "The barbarians forbid the use of prayer in their arena as they don't ~ value such methods!");
toggle(player,false);
return false;
}
}
if (!hasLevelStat(player, Skills.PRAYER, level) || !hasLevelStat(player, Skills.DEFENCE, defenceReq)) {
sendDialogue(player, "You need a <col=08088A>" + (!hasLevelStat(player, Skills.PRAYER, level) ? "Prayer level of " + level + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? " and a " : "") : "") + (!hasLevelStat(player, Skills.DEFENCE, defenceReq) ? "Defence level of " + defenceReq : "") + " to use " + StringUtils.formatDisplayName(name().toLowerCase().replace("_", " ")) + ".");
return false;