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
public Plugin<Object> newInstance(Object arg) throws Throwable {
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);
}
@@ -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,9 +322,12 @@ public final class InteractionPacket implements IncomingPacket {
PacketRepository.send(ClearMinimapFlag.class, new PlayerContext(player));
return;
}
if (!InteractionListeners.run(-1, InteractionListener.Companion.getPLAYER(), option.getName().toLowerCase(), player, target)) {
handleAIPLegion(player, 2, optionIndex, index);
target.getInteraction().handle(player, option);
}
}
/**
* Handles the ground item interaction.
@@ -100,6 +100,9 @@ public class ItemActionPacket implements IncomingPacket {
if(PluginInteractionManager.handle(player,event)){
return;
}
if(InteractionListeners.run(item, target, 4, player)){
return;
}
if(player.getZoneMonitor().useWith(used,player)){
return;
}
@@ -5,6 +5,7 @@ import core.game.node.Node
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.world.map.Location
import rs09.game.system.SystemLogger
interface InteractionListener : ContentInterface{
val ITEM: Int
@@ -15,6 +16,8 @@ interface InteractionListener : ContentInterface{
get() = 2
val GROUNDITEM: Int
get() = 3
val PLAYER: Int
get() = 4
fun on(id: Int, type: Int, vararg option: String, handler: (player: Player, node: Node) -> Boolean){
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) {
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
// 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) {
@@ -73,6 +79,11 @@ interface InteractionListener : ContentInterface{
SpadeDigListener.registerListener(location,method)
}
fun flagInstant() {
val name = this::class.java.name
InteractionListeners.instantClasses.add(name)
}
fun defineListeners()
companion object
@@ -81,5 +92,6 @@ interface InteractionListener : ContentInterface{
val SCENERY = 1
val NPC = 2
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 destinationOverrides = HashMap<String,(Entity, Node) -> Location>(100)
private val equipListeners = HashMap<String,(Player,Node) -> Boolean>(10)
val instantClasses = HashSet<String>()
@JvmStatic
fun add(id: Int, type: Int, option: Array<out String>, method: (Player,Node) -> Boolean){
@@ -161,7 +162,7 @@ object InteractionListeners {
@JvmStatic
fun run(used: Node, with: Node, type: Int,player: Player): Boolean{
val flag = when(type){
2 -> DestinationFlag.ENTITY
2, 4 -> DestinationFlag.ENTITY
1 -> DestinationFlag.OBJECT
else -> DestinationFlag.OBJECT
}
@@ -170,7 +171,8 @@ object InteractionListeners {
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) {
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
player.pulseManager.run(object : MovementPulse(player, with, flag, destOverride) {
override fun pulse(): Boolean {
@@ -206,6 +208,7 @@ object InteractionListeners {
@JvmStatic
fun run(id: Int, type: Int, option: String, player: Player, node: Node): Boolean{
val flag = when(type){
4 -> DestinationFlag.ENTITY
3 -> DestinationFlag.ITEM
2 -> DestinationFlag.ENTITY
1 -> null
@@ -226,7 +229,7 @@ object InteractionListeners {
return true
}
if(type != 0) {
if(type != 0 && !isInstant(method)) {
if(player.locks.isMovementLocked) return false
player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) {
override fun pulse(): Boolean {
@@ -257,4 +260,14 @@ object InteractionListeners {
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
}
}