Implemented pelting of rotten tomatoes

Added ability to define listeners for Player interactions and usewith interactions
Added ability to flag listeners instant (no path walking) execution
This commit is contained in:
Ceikry
2022-06-23 14:25:23 +00:00
committed by Ryan
parent c3a37d5cbf
commit 5cfe44d5cd
8 changed files with 155 additions and 155 deletions
@@ -101,7 +101,7 @@ public class ChristmasEvent extends HolidayEvent {
@Override @Override
public Plugin<Object> newInstance(Object arg) throws Throwable { public Plugin<Object> newInstance(Object arg) throws Throwable {
if (isActive()) { if (isActive()) {
ClassScanner.definePlugins(new ChristmasEventOptionPlugin(), new SnowmanItemHandler(), new SnowImpDialogue(), new SnowmanHatComponentPlugin(), new QueenOfSnowDialogue(), new SnowballItemPlugin(), new SnowmanNPC(), new PeltOptionHandler()); ClassScanner.definePlugins(new ChristmasEventOptionPlugin(), new SnowmanItemHandler(), new SnowImpDialogue(), new SnowmanHatComponentPlugin(), new QueenOfSnowDialogue(), new SnowmanNPC(), new PeltOptionHandler());
} }
return super.newInstance(arg); return super.newInstance(arg);
} }
@@ -1079,38 +1079,4 @@ public class ChristmasEvent extends HolidayEvent {
} }
} }
/**
* Handles the snow ball item plugin.
* @author Vexia
*
*/
public class SnowballItemPlugin implements Plugin<Object> {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
BALL_OF_SNOW.getDefinition().getHandlers().put("equipment", this);
return this;
}
@Override
public Object fireEvent(String identifier, Object... args) {
final Player player = (Player) args[0];
@SuppressWarnings("unused")
Item item = (Item) args[1];
final Item other = args.length == 2 ? null : (Item) args[2];
if (other != null) {
identifier = "equip";
}
switch (identifier) {
case "equip":
player.getInteraction().set(PELT_OPTION);
break;
case "unequip":
player.getInteraction().remove(PELT_OPTION);
break;
}
return true;
}
}
} }
@@ -322,8 +322,11 @@ public final class InteractionPacket implements IncomingPacket {
PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player)); PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player));
return; return;
} }
handleAIPLegion(player, 2, optionIndex, index);
target.getInteraction().handle(player, option); if (!InteractionListeners.run(-1, InteractionListener.Companion.getPLAYER(), option.getName().toLowerCase(), player, target)) {
handleAIPLegion(player, 2, optionIndex, index);
target.getInteraction().handle(player, option);
}
} }
/** /**
@@ -100,6 +100,9 @@ public class ItemActionPacket implements IncomingPacket {
if(PluginInteractionManager.handle(player,event)){ if(PluginInteractionManager.handle(player,event)){
return; return;
} }
if(InteractionListeners.run(item, target, 4, player)){
return;
}
if(player.getZoneMonitor().useWith(used,player)){ if(player.getZoneMonitor().useWith(used,player)){
return; return;
} }
@@ -5,6 +5,7 @@ import core.game.node.Node
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.world.map.Location import core.game.world.map.Location
import rs09.game.system.SystemLogger
interface InteractionListener : ContentInterface{ interface InteractionListener : ContentInterface{
val ITEM: Int val ITEM: Int
@@ -15,6 +16,8 @@ interface InteractionListener : ContentInterface{
get() = 2 get() = 2
val GROUNDITEM: Int val GROUNDITEM: Int
get() = 3 get() = 3
val PLAYER: Int
get() = 4
fun on(id: Int, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ fun on(id: Int, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
InteractionListeners.add(id,type,option,handler) InteractionListeners.add(id,type,option,handler)
@@ -37,6 +40,9 @@ interface InteractionListener : ContentInterface{
fun onUseAnyWith(type: Int, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) { fun onUseAnyWith(type: Int, vararg with: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) {
InteractionListeners.add(type, with, handler) InteractionListeners.add(type, with, handler)
} }
fun onUseWithPlayer(vararg used: Int, handler: (player: Player, used: Node, with: Node) -> Boolean) {
InteractionListeners.add(PLAYER, used, handler)
}
// Note: wildcard listeners incur overhead on every use-with interaction, only use them as a space-time tradeoff when something // Note: wildcard listeners incur overhead on every use-with interaction, only use them as a space-time tradeoff when something
// is actually supposed to have a response to every item used with it (e.g. imp boxes, certain quest npcs) // is actually supposed to have a response to every item used with it (e.g. imp boxes, certain quest npcs)
fun onUseWithWildcard(type: Int, predicate: (used: Int, with: Int) -> Boolean, handler: (player: Player, used: Node, with: Node) -> Boolean) { fun onUseWithWildcard(type: Int, predicate: (used: Int, with: Int) -> Boolean, handler: (player: Player, used: Node, with: Node) -> Boolean) {
@@ -73,6 +79,11 @@ interface InteractionListener : ContentInterface{
SpadeDigListener.registerListener(location,method) SpadeDigListener.registerListener(location,method)
} }
fun flagInstant() {
val name = this::class.java.name
InteractionListeners.instantClasses.add(name)
}
fun defineListeners() fun defineListeners()
companion object companion object
@@ -81,5 +92,6 @@ interface InteractionListener : ContentInterface{
val SCENERY = 1 val SCENERY = 1
val NPC = 2 val NPC = 2
val GROUNDITEM = 3 val GROUNDITEM = 3
val PLAYER = 4
} }
} }
@@ -18,6 +18,7 @@ object InteractionListeners {
private val useWithWildcardListeners = HashMap<Int, ArrayList<Pair<(Int, Int) -> Boolean, (Player, Node, Node) -> Boolean>>>(10) private val useWithWildcardListeners = HashMap<Int, ArrayList<Pair<(Int, Int) -> Boolean, (Player, Node, Node) -> Boolean>>>(10)
private val destinationOverrides = HashMap<String,(Entity, Node) -> Location>(100) private val destinationOverrides = HashMap<String,(Entity, Node) -> Location>(100)
private val equipListeners = HashMap<String,(Player,Node) -> Boolean>(10) private val equipListeners = HashMap<String,(Player,Node) -> Boolean>(10)
val instantClasses = HashSet<String>()
@JvmStatic @JvmStatic
fun add(id: Int, type: Int, option: Array<out String>, method: (Player,Node) -> Boolean){ fun add(id: Int, type: Int, option: Array<out String>, method: (Player,Node) -> Boolean){
@@ -161,7 +162,7 @@ object InteractionListeners {
@JvmStatic @JvmStatic
fun run(used: Node, with: Node, type: Int,player: Player): Boolean{ fun run(used: Node, with: Node, type: Int,player: Player): Boolean{
val flag = when(type){ val flag = when(type){
2 -> DestinationFlag.ENTITY 2, 4 -> DestinationFlag.ENTITY
1 -> DestinationFlag.OBJECT 1 -> DestinationFlag.OBJECT
else -> DestinationFlag.OBJECT else -> DestinationFlag.OBJECT
} }
@@ -170,7 +171,8 @@ object InteractionListeners {
var flipped = false var flipped = false
val method = get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false val method = if (with is Player) get(-1, used.id, 4) ?: return false
else get(used.id,with.id,type) ?: get(with.id,used.id,type).also { flipped = true } ?: return false
val destOverride = if(flipped) { val destOverride = if(flipped) {
getOverride(type, used.id, "use") ?: getOverride(type, with.id) ?: getOverride(type, "use") getOverride(type, used.id, "use") ?: getOverride(type, with.id) ?: getOverride(type, "use")
@@ -179,7 +181,7 @@ object InteractionListeners {
} }
if(type != 0) { if(type != 0 && !isUseWithInstant(method)) {
if(player.locks.isMovementLocked) return false if(player.locks.isMovementLocked) return false
player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) { player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
@@ -206,6 +208,7 @@ object InteractionListeners {
@JvmStatic @JvmStatic
fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{ fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{
val flag = when(type){ val flag = when(type){
4 -> DestinationFlag.ENTITY
3 -> DestinationFlag.ITEM 3 -> DestinationFlag.ITEM
2 -> DestinationFlag.ENTITY 2 -> DestinationFlag.ENTITY
1 -> null 1 -> null
@@ -226,7 +229,7 @@ object InteractionListeners {
return true return true
} }
if(type != 0) { if(type != 0 && !isInstant(method)) {
if(player.locks.isMovementLocked) return false if(player.locks.isMovementLocked) return false
player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) { player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) {
override fun pulse(): Boolean { override fun pulse(): Boolean {
@@ -257,4 +260,14 @@ object InteractionListeners {
useAnyWithListeners["$w:$type"] = handler useAnyWithListeners["$w:$type"] = handler
} }
} }
fun isInstant(handler: (Player, Node) -> Boolean): Boolean {
val className = handler.javaClass.name.substringBefore("$")
return instantClasses.contains(className)
}
fun isUseWithInstant(handler: (player: Player, used: Node, with: Node) -> Boolean): Boolean {
val className = handler.javaClass.name.substringBefore("$")
return instantClasses.contains(className)
}
} }
@@ -1,83 +0,0 @@
package rs09.game.interaction.player
import core.game.interaction.Interaction
import core.game.interaction.OptionHandler
import core.game.node.Node
import core.game.node.entity.impl.Projectile
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics
import core.plugin.Initializable
import core.plugin.Plugin
import org.rs09.consts.Items
import rs09.game.world.GameWorld
import rs09.tools.stringtools.colorize
val snowball = Item(Items.SNOWBALL_11951)
val THROW_ANIMATION = Animation(7530)
val THROW_GRAPHICS = Graphics(860)
@Initializable
class PeltOptionPlugin : OptionHandler() {
override fun newInstance(arg: Any?): Plugin<Any> {
return this
}
override fun handle(player: Player?, node: Node?, option: String?): Boolean {
player ?: return false
val other = node?.asPlayer()
val delay = 41
val speed = 60
val distance = Location.getDistance(player.location, other?.getLocation()).toInt()
val projectileSpeed = delay + speed + distance * 5
val hitDelay = (projectileSpeed * .02857).toInt()
if(!Pathfinder.find(player,other,false, Pathfinder.PROJECTILE).isSuccessful){
player.dialogueInterpreter.sendDialogue("You can't reach them!")
return true
}
if(player.inventory.remove(snowball) || player.equipment.remove(snowball)){
player.lock()
GameWorld.Pulser.submit(object : Pulse(hitDelay + 1){
override fun pulse(): Boolean {
other?.animator?.graphics(Graphics(1282))
return true
}
})
GameWorld.Pulser.submit(object : Pulse() {
var counter = 0
override fun pulse(): Boolean {
when (counter++) {
0 -> {
player.lock()
player.face(other)
player.animator.forceAnimation(THROW_ANIMATION)
}
1 -> {
Projectile.create(player, other, 861, 30, 10).send()
player.face(player)
player.unlock()
}
5 -> other?.sendMessage(colorize("%R${player.username} has pelted you with a snowball."))
6 -> return true
}
return false
}
})
} else {
player.sendMessage("You have no more snowballs.")
Interaction.sendOption(player, 0, "null")
}
return true
}
override fun isWalk(): Boolean {
return false
}
}
@@ -0,0 +1,117 @@
package rs09.game.node.entity.equipment
import api.*
import core.game.interaction.Interaction
import core.game.interaction.Option
import core.game.node.Node
import core.game.node.entity.impl.Projectile
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.path.Pathfinder
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Items
import rs09.game.interaction.InteractionListener
class PlayerPeltables : InteractionListener {
companion object {
private const val PROJECTILE_DELAY = 41
private const val PROJECTILE_SPEED = 60
private const val PROJECTILE_TIME_CONST = .02857
private const val PROJECTILE_DISTANCE_MULT = 5
private val PELTABLES = intArrayOf(Items.ROTTEN_TOMATO_2518, Items.SNOWBALL_11951)
}
override fun defineListeners() {
onEquip(PELTABLES, ::setPlayerOps)
onUnequip(PELTABLES, ::removePlayerOps)
on("pelt", PLAYER, ::handlePeltInteraction)
onUseWithPlayer(*PELTABLES) {player, used, with -> handlePeltInteraction(player, with, used) }
flagInstant()
}
private fun setPlayerOps(player: Player, _node: Node) : Boolean {
player.interaction.set(Option("Pelt", 0))
return true
}
private fun removePlayerOps(player: Player, _node: Node) : Boolean {
Interaction.sendOption(player, 0, "null")
return true
}
private fun handlePeltInteraction(player: Player, node: Node, usedPeltable: Node? = null) : Boolean {
val peltable = usedPeltable?.asItem() ?: getPeltable(player) ?: return removePlayerOps(player, node)
val gfx = getPeltableGfx(peltable.id)
val other = node.asPlayer()
if (!Pathfinder.find(player, other, false, Pathfinder.PROJECTILE).isSuccessful) {
sendDialogue(player, "You can't reach them!")
return true
}
val distance = player.location.getDistance(other.location)
val projectileSpeed = PROJECTILE_DELAY + PROJECTILE_SPEED + distance * PROJECTILE_DISTANCE_MULT
val hitDelay = (projectileSpeed * PROJECTILE_TIME_CONST).toInt()
if (removeItem(player, peltable, Container.INVENTORY) || removeItem(player, peltable, Container.EQUIPMENT)) {
lock(player, hitDelay)
submitWorldPulse(PeltingPulse(player, other, gfx, hitDelay, peltable.id))
}
return true
}
class PeltingPulse(val player: Player, val other: Player, val gfx: IntArray, val hitDelay: Int, val peltable: Int) : Pulse() {
private val throwAnimation = getPeltableAnim(peltable)
private var ticks = 0
override fun pulse(): Boolean {
when (ticks++) {
0 -> {
player.face(other)
visualize(player, throwAnimation, gfx[0])
}
1 -> {
Projectile.create(player, other, gfx[1], 30, 10).send()
face(player, player) //reset face flag lel
unlock(player)
}
hitDelay -> {
if (gfx[2] != -1) other.graphics(Graphics(gfx[2]))
sendMessage(other, "${player.username} has hit you with a ${getItemName(peltable).toLowerCase()}.")
return true
}
}
return false
}
private fun getPeltableAnim(id: Int) : Int {
return when (id) {
Items.SNOWBALL_11951 -> 7530
Items.ROTTEN_TOMATO_2518 -> 385
else -> -1
}
}
}
private fun getPeltableGfx(id: Int): IntArray {
return when (id) {
Items.SNOWBALL_11951 -> intArrayOf(-1, 861, 1282)
Items.ROTTEN_TOMATO_2518 -> intArrayOf(-1, 29, 31)
else -> IntArray(3) {-1}
}
}
private fun getPeltable(player: Player): Item? {
val equipped = getItemFromEquipment(player, EquipmentSlot.WEAPON) ?: return null
val id = equipped.id
if (id !in PELTABLES) return null
return equipped
}
}
@@ -1,31 +0,0 @@
package rs09.game.node.entity.equipment
import core.cache.def.impl.ItemDefinition
import core.game.interaction.Interaction
import core.game.interaction.Option
import rs09.game.interaction.player.PeltOptionPlugin
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.plugin.Initializable
import core.plugin.Plugin
@Initializable
class SnowballEquipmentPlugin : Plugin<Item> {
override fun newInstance(arg: Item?): Plugin<Item> {
ItemDefinition.forId(11951).handlers["equipment"] = this
return this
}
override fun fireEvent(identifier: String?, vararg args: Any?): Any {
val player = args[0] as Player
when(identifier){
"equip" -> {
player.getInteraction().set(Option("Pelt", 0).setHandler(PeltOptionPlugin()))
}
"unequip" -> {
Interaction.sendOption(player, 0, "null")
}
}
return true
}
}