Implemented Freaky Forester random event

Added admin ::freak command to trigger Freaky Forester
This commit is contained in:
Zerken
2023-07-30 02:52:45 +00:00
committed by Ryan
parent 32e1111fc7
commit 9b08f2175b
13 changed files with 350 additions and 10 deletions
@@ -5,6 +5,7 @@ import content.global.ame.events.MysteriousOldManNPC
import content.global.ame.events.certer.CerterNPC
import content.global.ame.events.drilldemon.SeargentDamienNPC
import content.global.ame.events.evilchicken.EvilChickenNPC
import content.global.ame.events.freakyforester.FreakyForesterNPC
import content.global.ame.events.genie.GenieNPC
import content.global.ame.events.rivertroll.RiverTrollRENPC
import content.global.ame.events.rockgolem.RockGolemRENPC
@@ -40,6 +41,7 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
DRILL_DEMON(npc = SeargentDamienNPC()),
EVIL_CHICKEN(npc = EvilChickenNPC()),
SURPRISE_EXAM(npc = MysteriousOldManNPC(), type = "sexam"),
FREAKY_FORESTER(npc = FreakyForesterNPC(), skillId = Skills.WOODCUTTING),
TREE_SPIRIT(npc = TreeSpiritRENPC(), skillId = Skills.WOODCUTTING),
RIVER_TROLL(RiverTrollRENPC(), skillId = Skills.FISHING),
ROCK_GOLEM(RockGolemRENPC(), skillId = Skills.MINING),
@@ -0,0 +1,62 @@
package content.global.ame.events.freakyforester
import core.api.*
import org.rs09.consts.NPCs
import org.rs09.consts.Scenery
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.Entity
import core.game.system.task.Pulse
import core.game.world.map.zone.ZoneBorders
import core.game.world.map.zone.ZoneRestriction
import org.rs09.consts.Items
class FreakListeners : InteractionListener, MapArea {
private val exitPortal = Scenery.PORTAL_8972
private val freakNpc = NPCs.FREAKY_FORESTER_2458
private val pheasants = intArrayOf(NPCs.PHEASANT_2459,NPCs.PHEASANT_2460,NPCs.PHEASANT_2461,NPCs.PHEASANT_2462)
override fun defineListeners() {
on(freakNpc,IntType.NPC,"talk-to"){player, node ->
if (getAttribute(player, FreakUtils.freakTask, -1) == -1) FreakUtils.giveFreakTask(player)
openDialogue(player, FreakyForesterDialogue(), node.asNpc())
return@on true
}
on(pheasants,IntType.NPC,"attack") { player, node ->
if (getAttribute(player,FreakUtils.freakComplete, false)) {
sendMessage(player,"You don't need to attack any more pheasants. You're allowed to leave.")
} else if (inInventory(player, Items.RAW_PHEASANT_6178) || inInventory(player, Items.RAW_PHEASANT_6179) || getAttribute(player, FreakUtils.pheasantKilled, false)){
sendMessage(player,"You don't need to attack any more pheasants.")
} else {
player.attack(node.asNpc())
}
return@on true
}
on(exitPortal,IntType.SCENERY,"enter") { player, _ ->
if (getAttribute(player,FreakUtils.freakComplete,false)) {
FreakUtils.cleanup(player)
submitWorldPulse(object : Pulse(2) {
override fun pulse(): Boolean {
FreakUtils.reward(player)
return true
}
})
return@on true
} else sendMessage(player,"A supernatural force prevents you from leaving.")
return@on true
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(ZoneBorders(2587, 4758, 2616,4788))
}
override fun getRestrictions(): Array<ZoneRestriction> {
return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS)
}
override fun areaEnter(entity: Entity) {
entity.locks.lockTeleport(1000000)
}
}
@@ -0,0 +1,57 @@
package content.global.ame.events.freakyforester
import core.api.*
import org.rs09.consts.Items
import org.rs09.consts.NPCs
import core.game.node.entity.player.Player
import core.game.world.map.Location
import core.tools.RandomFunction
object FreakUtils{
const val freakNpc = NPCs.FREAKY_FORESTER_2458
const val freakPreviousLoc = "/save:freakyf:location"
const val freakTask = "/save:freakyf:task"
const val freakComplete = "/save:freakyf:complete"
const val pheasantKilled = "freakyf:killed"
fun giveFreakTask(player: Player) {
when(RandomFunction.getRandom(4)) {
0 -> setAttribute(player, freakTask, NPCs.PHEASANT_2459)
1 -> setAttribute(player, freakTask, NPCs.PHEASANT_2460)
2 -> setAttribute(player, freakTask, NPCs.PHEASANT_2461)
3 -> setAttribute(player, freakTask, NPCs.PHEASANT_2462)
else -> setAttribute(player, freakTask, NPCs.PHEASANT_2459)
}
player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc)
}
fun teleport(player: Player) {
setAttribute(player, freakPreviousLoc, player.location)
teleport(player, Location.create(2599, 4777 ,0))
}
fun cleanup(player: Player) {
player.locks.unlockTeleport()
player.properties.teleportLocation = getAttribute(player,freakPreviousLoc,null)
removeAttributes(player, freakPreviousLoc, freakTask, freakComplete, pheasantKilled)
removeAll(player, Items.RAW_PHEASANT_6178)
removeAll(player, Items.RAW_PHEASANT_6178, Container.BANK)
removeAll(player, Items.RAW_PHEASANT_6179)
removeAll(player, Items.RAW_PHEASANT_6179, Container.BANK)
}
fun reward(player: Player){
val hasHat = hasAnItem(player, Items.LEDERHOSEN_HAT_6182).container != null
val hasTop = hasAnItem(player, Items.LEDERHOSEN_TOP_6180).container != null
val hasShort = hasAnItem(player, Items.LEDERHOSEN_SHORTS_6181).container != null
sendNPCDialogue(player, freakNpc, "You get a lederhosen item as a reward for your help, many thanks!")
when{
(!hasHat) -> addItemOrDrop(player, Items.LEDERHOSEN_HAT_6182, 1)
(!hasTop) -> addItemOrDrop(player, Items.LEDERHOSEN_TOP_6180, 1)
(!hasShort) -> addItemOrDrop(player, Items.LEDERHOSEN_SHORTS_6181, 1)
else ->{
sendNPCDialogue(player, freakNpc, "You get some money for your help, many thanks!")
addItemOrDrop(player, Items.COINS_995, 500)
}
}
}
}
@@ -0,0 +1,27 @@
package content.global.ame.events.freakyforester
import core.api.*
import core.game.dialogue.DialogueFile
import core.game.dialogue.FacialExpression
import core.tools.END_DIALOGUE
import org.rs09.consts.Items
import org.rs09.consts.NPCs
class FreakyForesterDialogue : DialogueFile(){
override fun handle(componentID: Int, buttonID: Int) {
if (removeItem(player!!, Items.RAW_PHEASANT_6179) && !getAttribute(player!!, FreakUtils.freakComplete, false)) {
npcl(FacialExpression.NEUTRAL, "That's not the right one.").also { stage = END_DIALOGUE }
setAttribute(player!!, FreakUtils.pheasantKilled, false)
} else if (removeItem(player!!, Items.RAW_PHEASANT_6178) || getAttribute(player!!, FreakUtils.freakComplete, false)) {
npcl(FacialExpression.NEUTRAL, "Thanks, ${player!!.username}, you may leave the area now.").also { stage = END_DIALOGUE }
sendChat(findNPC(FreakUtils.freakNpc)!!, "Thanks, ${player!!.username}, you may leave the area now.")
setAttribute(player!!, FreakUtils.freakComplete, true)
} else when (getAttribute(player!!, FreakUtils.freakTask, -1)) {
NPCs.PHEASANT_2459 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the one tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE }
NPCs.PHEASANT_2460 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the two tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE }
NPCs.PHEASANT_2461 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the three tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE }
NPCs.PHEASANT_2462 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the four tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE }
}
}
}
@@ -0,0 +1,44 @@
package content.global.ame.events.freakyforester
import content.global.ame.RandomEventManager
import content.global.ame.RandomEventNPC
import core.api.*
import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable
import core.game.node.entity.npc.NPC
import core.game.system.task.Pulse
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Sounds
class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) {
override fun init() {
super.init()
lock(player, 8)
submitWorldPulse(object : Pulse() {
var counter = 0
override fun pulse(): Boolean {
when (counter++) {
0 -> sendChat("Ah, ${player.username}, just the person I need!")
4 -> {
sendGraphics(Graphics(308, 100, 50), player.location)
animate(player,714)
playAudio(player, Sounds.TELEPORT_ALL_200)
}
7 -> {
FreakUtils.teleport(player)
FreakUtils.giveFreakTask(player)
RandomEventManager.getInstance(player)!!.event?.terminate()
openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc)
resetAnimator(player)
}
}
return false
}
})
}
override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(FreakyForesterDialogue(),npc)
}
}
@@ -0,0 +1,37 @@
package content.global.ame.events.freakyforester
import core.api.getAttribute
import core.api.sendMessage
import core.api.sendNPCDialogue
import core.api.setAttribute
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
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.item.Item
import org.rs09.consts.Items
import org.rs09.consts.NPCs
class PheasantBehavior : NPCBehavior(NPCs.PHEASANT_2459, NPCs.PHEASANT_2460, NPCs.PHEASANT_2461, NPCs.PHEASANT_2462) {
override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList<Item>) {
val assignedPheasant = getAttribute(killer, FreakUtils.freakTask, -1)
if (assignedPheasant == -1) return
drops.removeLast()
if (assignedPheasant == self.id) {
drops.add(Item (Items.RAW_PHEASANT_6178))
} else {
drops.add(Item (Items.RAW_PHEASANT_6179))
}
setAttribute(killer, FreakUtils.pheasantKilled, true)
}
override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {
state.estimatedHit = 5
state.secondaryHit = 0
}
override fun getXpMultiplier(self: NPC, attacker: Entity): Double {
return 0.0
}
}
@@ -69,7 +69,7 @@ class ImplingBehavior : NPCBehavior (*Impling.getIds()) {
}
//manually clear any rolled drops so any drops set by JSON are properly ignored.
override fun onDropTableRolled (self: NPC, drops: ArrayList<Item>) {
override fun onDropTableRolled (self: NPC, killer: Entity, drops: ArrayList<Item>) {
drops.clear()
}
@@ -483,10 +483,13 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
is Player -> {player = entity; attStyle = entity.properties.attackStyle.style}
else -> return
}
if (victim is NPC) EXPERIENCE_MOD *= victim.behavior.getXpMultiplier(victim, player)
if (state != null) {
val hit = state.totalDamage
if (entity is Player) {
player.skills.addExperience(Skills.HITPOINTS, hit * 1.33, true)
var experience = hit * 1.33
if (victim is NPC) experience *= victim.behavior.getXpMultiplier(victim, player)
player.skills.addExperience(Skills.HITPOINTS, experience, true)
}
var skill = -1
@@ -513,8 +516,10 @@ abstract class CombatSwingHandler(var type: CombatStyle?) {
WeaponInterface.STYLE_CAST -> skill = Skills.MAGIC
WeaponInterface.STYLE_DEFENSIVE_CAST -> {
player.skills.addExperience(Skills.MAGIC, hit * 1.33, true)
player.skills.addExperience(Skills.DEFENCE, hit.toDouble(), true)
var experience = hit.toDouble()
if (victim is NPC) experience *= victim.behavior.getXpMultiplier(victim, player)
player.skills.addExperience(Skills.MAGIC, experience * 1.33, true)
player.skills.addExperience(Skills.DEFENCE, experience, true)
return
}
}
@@ -99,9 +99,10 @@ open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
/**
* Called after this NPC's drop table is rolled, but before the items are actually dropped, so the list can be manipulated.
* @param self the NPC instance this behavior belongs to
* @param killer the killer of the npc
* @param drops the generated list of drops for this roll of the table
*/
open fun onDropTableRolled(self: NPC, drops: ArrayList<Item>) {}
open fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList<Item>) {}
/**
* Called by combat-related code to check if this NPC can be attacked by the `attacker` entity.
@@ -139,4 +140,14 @@ open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface {
open fun getClippingSupplier(self: NPC) : ClipMaskSupplier? {
return StandardClipMaskSupplier
}
/**
* Modifies the combat experience gained from killing an entity
* @param self the NPC instance this behavior belongs to
* @param attacker entity attacking the npc
* @return multiplier for final xp gain
*/
open fun getXpMultiplier(self: NPC, attacker: Entity) : Double {
return 1.0
}
}
@@ -75,7 +75,7 @@ public final class NPCDropTables {
public void drop(NPC npc, Entity looter) {
Player p = looter instanceof Player ? (Player) looter : null;
ArrayList<Item> drops = table.roll(looter);
npc.behavior.onDropTableRolled(npc, drops);
npc.behavior.onDropTableRolled(npc, looter, drops);
drops.forEach(item -> createDrop(item,p,npc,npc.getDropLocation()));
}
@@ -551,6 +551,11 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){
content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init()
}
define("freak", Privilege.ADMIN){ player, _ ->
content.global.ame.RandomEventManager.getInstance(player)!!.event = RandomEvents.FREAKY_FORESTER.npc.create(player)
content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init()
}
define("revent", Privilege.ADMIN){ player, _ ->
println(player.pulseManager.current)
content.global.ame.RandomEventManager.getInstance(player)!!.fireEvent()