Rewrote "emptying" contents of many items

Implemented emptying plant cure
Rewrote DFS handler
Corrected DFS max hit to be 25 instead of 26
Corrected DFS attack cooldown to be 2 mintues instead of 30 secs
Implemented the animation when emptying charges from the DFS
Implemented a server config (better_dfs) to preserve previous functionality (enabled by default)
This commit is contained in:
Zerken
2023-09-12 03:02:03 +00:00
committed by Ryan
parent 0028be9cda
commit e60eecc6bf
7 changed files with 242 additions and 257 deletions
@@ -0,0 +1,147 @@
package content.global.handlers.item
import content.global.handlers.item.equipment.special.DragonfireSwingHandler
import core.ServerConstants
import core.api.*
import core.game.container.impl.EquipmentContainer
import core.game.global.action.DropListener
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.entity.Entity
import core.game.node.entity.combat.BattleState
import core.game.node.entity.combat.CombatPulse.Companion.swing
import core.game.node.entity.combat.InteractionType
import core.game.node.entity.combat.equipment.SwitchAttack
import core.game.node.entity.impl.Projectile
import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.node.item.ItemPlugin
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 core.tools.minutesToTicks
import core.tools.secondsToTicks
import org.rs09.consts.Items
import org.rs09.consts.Sounds
/**
* Handles the dragonfire shield options.
*/
class DragonfireShieldListener: InteractionListener {
val dragonfireShields = intArrayOf(Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11284)
val dfsEmptyAnim = 6700
val dfsEmptyGfx = 1160
val dfsRecharge = if (ServerConstants.BETTER_DFS) secondsToTicks(30) else minutesToTicks(2)
override fun defineListeners() {
on(dragonfireShields, IntType.ITEM, "operate") { player, node ->
val usingAttack = !getAttribute(player, "dfs_spec", false)
if (!usingAttack) {
if (!player.settings.isSpecialToggled) {
setVarp(player, 301, 0)
}
player.removeAttribute("dfs_spec")
player.properties.combatPulse.temporaryHandler = null
return@on true
}
val notCharged = node.asItem().id == Items.DRAGONFIRE_SHIELD_11284 || node.asItem().charge < 20
if (notCharged) {
sendMessage(player, "Your shield has no charges left.")
return@on true
}
if (player.locks.isLocked("dfs_recharge")) {
sendMessage(player, "Your dragonfire shield is recharging.")
return@on true
}
setVarp(player, 301, 1)
player.setAttribute("dfs_spec", true)
val attack = SwitchAttack(null, Animation.create(6696), Graphics.create(1165), Graphics(1167, 96), Projectile.create(player, null, 1166, 36, 36, 80, 70, 0, 11))
val handler: DragonfireSwingHandler = object : DragonfireSwingHandler(false, 25, attack, true) {
override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int {
if (entity is Player) {
if (!player.settings.isSpecialToggled) {
setVarp(player, 301, 0)
}
removeAttribute(player, "dfs_spec")
val shield = player.equipment[EquipmentContainer.SLOT_SHIELD]
if (shield == null || shield.id != Items.DRAGONFIRE_SHIELD_11283) {
return -1
}
playGlobalAudio(entity.getLocation(), Sounds.DRAGONSLAYER_SHIELDFIRE_3761)
delayEntity(player, 3)
shield.charge -= 20
if (shield.charge < 20 && node.asItem().slot == EquipmentContainer.SLOT_SHIELD) {
replaceSlot(player, node.asItem().slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283), Container.EQUIPMENT)
}
EquipmentContainer.updateBonuses(player)
player.locks.lock("dfs_recharge", dfsRecharge)
}
return super.swing(entity, victim, state)
}
override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) {
playGlobalAudio(victim!!.location, Sounds.FIRESTRIKE_HIT_161, 20)
super.visualizeImpact(entity, victim, state)
}
}
attack.handler = handler
val victim = player.properties.combatPulse.getVictim()
if (player.properties.combatPulse.isAttacking && handler.canSwing(player, victim!!) == InteractionType.STILL_INTERACT) {
swing(player, victim, handler)
return@on true
}
player.properties.combatPulse.temporaryHandler = handler
return@on true
}
on(dragonfireShields, IntType.ITEM, "empty") { player, node ->
replaceSlot(player, node.asItem().slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283))
visualize(player, dfsEmptyAnim, dfsEmptyGfx)
sendMessage(player, "You release the charges.")
playGlobalAudio(player.location, Sounds.DRAGONSLAYER_SHIELD_EMPTY_3760)
return@on true
}
on(dragonfireShields, IntType.ITEM, "inspect") { player, node ->
if (node.asItem().id == Items.DRAGONFIRE_SHIELD_11284) {
sendMessage(player, "The shield has no charges.")
return@on true
}
sendMessage(player, "The shield has " + node.asItem().charge / 20 + " charges.")
return@on true
}
on(dragonfireShields, IntType.ITEM, "drop") { player, node ->
var shield = node.asItem()
val slot = shield.slot
if (shield.id == Items.DRAGONFIRE_SHIELD_11283) {
replaceSlot(player, shield.slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283))
shield = player.inventory.get(slot)
}
DropListener.drop(player, shield)
return@on true
}
}
}
/**
* Drops the uncharged dragonfire shield item id on death.
*/
@Initializable
class DFSItemPlugin : ItemPlugin() {
@Throws(Throwable::class)
override fun newInstance(arg: Any?): Plugin<Any> {
register(Items.DRAGONFIRE_SHIELD_11283)
return this
}
override fun getDeathItem(item: Item?): Item {
return Item(Items.DRAGONFIRE_SHIELD_11284)
}
}
@@ -1,149 +0,0 @@
package content.global.handlers.item;
import core.cache.def.impl.ItemDefinition;
import core.game.container.impl.EquipmentContainer;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
import core.game.node.entity.Entity;
import core.game.node.entity.combat.BattleState;
import core.game.node.entity.combat.InteractionType;
import core.game.node.entity.combat.equipment.SwitchAttack;
import content.global.handlers.item.equipment.special.DragonfireSwingHandler;
import core.game.node.entity.impl.Projectile;
import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.game.node.item.ItemPlugin;
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 core.game.node.entity.combat.CombatPulse;
import core.game.world.GameWorld;
import core.plugin.ClassScanner;
import org.rs09.consts.Sounds;
import java.util.concurrent.TimeUnit;
import static core.api.ContentAPIKt.*;
/**
* Handles the dragonfire shield options.
* @author Emperor
*/
@Initializable
public final class DragonfireShieldPlugin extends OptionHandler {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ItemDefinition.forId(11283).getHandlers().put("option:empty", this);
ItemDefinition.forId(11283).getHandlers().put("option:inspect", this);
ItemDefinition.forId(11283).getHandlers().put("option:operate", this);
ItemDefinition.forId(11284).getHandlers().put("option:inspect", this);
ItemDefinition.forId(11284).getHandlers().put("option:operate", this);
ClassScanner.definePlugin(new DFSItemPlugin());
return this;
}
@Override
public boolean handle(Player player, Node node, String option) {
Item item = (Item) node;
switch (option) {
case "operate":
boolean usingAttack = !player.getAttribute("dfs_spec", false);
if (!usingAttack) {
if (!player.getSettings().isSpecialToggled()) {
setVarp(player, 301, 0);
}
player.removeAttribute("dfs_spec");
player.getProperties().getCombatPulse().setTemporaryHandler(null);
return true;
}
boolean notCharged = item.getId() == 11284 || item.getCharge() < 20;
if (player.getSavedData().getGlobalData().getOverChargeDelay() <= System.currentTimeMillis()) {
item.setCharge(1020);
player.sendMessage("You use the power from the overcharge lords & charge your shield.");
notCharged = false;
player.getSavedData().getGlobalData().setOverChargeDelay(System.currentTimeMillis() + (GameWorld.getSettings().isDevMode() ? TimeUnit.SECONDS.toMillis(10) : TimeUnit.MINUTES.toMicros(10)));
}
if (notCharged) {
player.getPacketDispatch().sendMessage("Your shield has no charges left.");
return true;
}
if (player.getLocks().isLocked("dfs_recharge")) {
player.getPacketDispatch().sendMessage("Your dragonfire shield is recharging.");
return true;
}
setVarp(player, 301, 1);
player.setAttribute("dfs_spec", true);
SwitchAttack attack = new SwitchAttack(null, Animation.create(6696), Graphics.create(1165), new Graphics(1167, 96), Projectile.create(player, null, 1166, 36, 36, 80, 70, 0, 11));
DragonfireSwingHandler handler = new DragonfireSwingHandler(false, 26, attack, true) {
@Override
public int swing(Entity entity, Entity victim, BattleState state) {
if (entity instanceof Player) {
Player player = (Player) entity;
if (!player.getSettings().isSpecialToggled()) {
setVarp(player, 301, 0);
}
player.removeAttribute("dfs_spec");
playGlobalAudio(entity.getLocation(), Sounds.DRAGONSLAYER_SHIELDFIRE_3761);
Item shield = player.getEquipment().get(EquipmentContainer.SLOT_SHIELD);
if (shield == null || shield.getId() != 11283) {
return -1;
}
shield.setCharge(shield.getCharge() - 20);
if (shield.getCharge() < 1) {
player.getEquipment().replace(new Item(11284), EquipmentContainer.SLOT_SHIELD);
}
EquipmentContainer.updateBonuses(player);
player.getLocks().lock("dfs_recharge",50);
}
return super.swing(entity, victim, state);
}
@Override
public void visualizeImpact(Entity entity, Entity victim, BattleState state) {
playGlobalAudio(victim.getLocation(), Sounds.FIRESTRIKE_HIT_161, 20);
super.visualizeImpact(entity, victim, state);
}
};
attack.setHandler(handler);
Entity victim = player.getProperties().getCombatPulse().getVictim();
if (player.getProperties().getCombatPulse().isAttacking() && handler.canSwing(player, victim) == InteractionType.STILL_INTERACT) {
CombatPulse.Companion.swing(player, victim, handler);
return true;
}
player.getProperties().getCombatPulse().setTemporaryHandler(handler);
return true;
case "empty":
player.getInventory().replace(new Item(11284), item.getSlot());
player.graphics(Graphics.create(1160));
player.getPacketDispatch().sendMessage("You release the charges.");
playGlobalAudio(player.getLocation(), Sounds.DRAGONSLAYER_SHIELD_EMPTY_3760);
return true;
case "inspect":
if (item.getId() == 11284) {
player.getPacketDispatch().sendMessage("The shield has no charges.");
return true;
}
player.getPacketDispatch().sendMessage("The shield has " + (item.getCharge() / 20) + " charges.");
return true;
}
return false;
}
public class DFSItemPlugin extends ItemPlugin {
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
register(11283);
return this;
}
@Override
public Item getDeathItem(Item item) {
return new Item(11284);
}
}
}
@@ -0,0 +1,89 @@
package content.global.handlers.item
import core.api.*
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.node.item.Item
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.Items
import org.rs09.consts.Sounds
import java.util.*
/**
* Handles items with "empty" options
*/
class EmptyOptionListener : InteractionListener {
override fun defineListeners() {
on(IntType.ITEM, "empty", "empty bowl") { player, node ->
if (node.name.contains("brew") || node.name.contains("potion") || node.name.lowercase(Locale.getDefault()).contains("poison") || node.name.lowercase(Locale.getDefault()).contains("serum") || node.name.contains("cure") || node.name.contains("mix") || node.name.contains("balm")) {
if (removeItem(player, node.id)) {
addItem(player, EmptyItem.getEmpty(Items.POTION_195)!!)
playAudio(player, EmptyItem.getEmptyAudio(Items.POTION_195)!!)
}
return@on true
}
if (EmptyItem.emptyItemMap[node.id] != null) {
if (removeItem(player, node.id)) {
addItem(player, EmptyItem.getEmpty(node.id)!!)
if (EmptyItem.getEmptyAudio(node.id) != -1) playAudio(player, EmptyItem.getEmptyAudio(node.id)!!)
EmptyItem.getEmptyMessage(node.id)?.let { sendMessage(player, it) }
}
}
return@on true
}
}
enum class EmptyItem(var fullId: Int, var emptyId: Int, var emptyMessage: String, var audioId: Int = -1) {
POT_OF_FLOUR(Items.POT_OF_FLOUR_1933, Items.EMPTY_POT_1931, "You empty the contents of the pot onto the floor."),
POT_OF_CORNFLOUR(Items.POT_OF_CORNFLOUR_7468, Items.EMPTY_POT_1931, "You empty the contents of the pot onto the floor."),
BONE_MEAL(Items.BONEMEAL_4255, Items.EMPTY_POT_1931, "You empty the pot of crushed bones."),
BUCKET_OF_SAND(Items.BUCKET_OF_SAND_1783, Items.BUCKET_1925, "You empty the contents of the bucket onto the floor.", Sounds.SAND_BUCKET_2584),
BUCKET_OF_MILK(Items.BUCKET_OF_MILK_1927, Items.BUCKET_1925, "You empty the contents of the bucket onto the floor.", Sounds.LIQUID_2401),
BUCKET_OF_WATER(Items.BUCKET_OF_WATER_1929, Items.BUCKET_1925, "You empty the contents of the bucket onto the floor.", Sounds.LIQUID_2401),
BUCKET_OF_COMPOST(Items.COMPOST_6032, Items.BUCKET_1925, "You empty the bucket of compost."),
BUCKET_OF_SUPERCOMPOST(Items.SUPERCOMPOST_6034, Items.BUCKET_1925, "You empty the bucket of supercompost."),
BUCKET_OF_SLIME(Items.BUCKET_OF_SLIME_4286, Items.BUCKET_1925, "You empty the contents of the bucket on the floor.", Sounds.LIQUID_2401),
VIAL_OF_WATER(Items.VIAL_OF_WATER_227, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
BOWL_OF_WATER(Items.BOWL_OF_WATER_1921, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
JUG_OF_WATER(Items.JUG_OF_WATER_1937, Items.JUG_1935, "You empty the contents of the jug onto the floor.", Sounds.LIQUID_2401),
BURNT_PIE(Items.BURNT_PIE_2329, Items.PIE_DISH_2313, "You empty the pie dish."),
POTION(Items.POTION_195, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401),
BURNT_STEW(Items.BURNT_STEW_2005, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA(Items.NETTLE_TEA_4239, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_WATER(Items.NETTLE_WATER_4237, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
NETTLE_TEA_MILKY(Items.NETTLE_TEA_4240, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
BURNT_CURRY(Items.BURNT_CURRY_2013, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401),
BURNT_GNOMEBOWL(Items.BURNT_GNOMEBOWL_2175, Items.GNOMEBOWL_MOULD_2166, "You empty the contents of the gnomebowl onto the floor."),
BURNT_EGG(Items.BURNT_EGG_7090, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."),
BURNT_ONION(Items.BURNT_ONION_7092, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."),
BURNT_MUSHROOM(Items.BURNT_MUSHROOM_7094, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.");
companion object {
var emptyItemMap = HashMap<Int, Int?>()
var emptyMessageMap = HashMap<Int, String>()
var emptyAudioMap = HashMap<Int, Int>()
init {
for (item in values()) {
emptyItemMap.putIfAbsent(item.fullId, item.emptyId)
emptyMessageMap.putIfAbsent(item.fullId, item.emptyMessage)
emptyAudioMap.putIfAbsent(item.fullId, item.audioId)
}
}
fun getEmpty(id: Int): Int? {
return emptyItemMap[id]
}
fun getEmptyMessage(id: Int): String? {
return emptyMessageMap[id]
}
fun getEmptyAudio(id: Int): Int? {
return emptyAudioMap[id]
}
}
}
}
@@ -1,108 +0,0 @@
package content.global.handlers.item;
import core.cache.def.impl.ItemDefinition;
import core.game.interaction.OptionHandler;
import core.game.node.Node;
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.Sounds;
import java.util.HashMap;
import static core.api.ContentAPIKt.playAudio;
/**
* Handles items with "empty" options
* @author ceik
*/
@Initializable
public final class EmptyOptionPlugin extends OptionHandler {
public static final int BUCKET = 1925;
public static final int VIAL = 229;
public static final String BUCKET_EMPTY_MSG = "You empty the contents of the bucket onto the floor.";
@Override
public boolean handle(Player player, Node node, String option) {
if (node.getName().contains("brew") || node.getName().contains("potion") || node.getName().toLowerCase().contains("poison") || node.getName().contains("serum")) {
player.getInventory().remove(node.asItem());
player.getInventory().add(EmptyItem.getEmpty(91));
return true;
}
if(EmptyItem.emptyItemMap.get(node.getId()) != null){
player.getInventory().remove(node.asItem());
playAudio(player, Sounds.LIQUID_2401, 0 ,1);
player.getInventory().add(EmptyItem.getEmpty(node.getId()));
player.getPacketDispatch().sendMessage(EmptyItem.getEmptyMessage(node.getId()));
} else {
player.debug("Unhandled empty option! ITEM ID: " + node.getId());
}
return true;
}
@Override
public boolean isWalk() {
return false;
}
@Override
public Plugin<Object> newInstance(Object arg) throws Throwable {
ItemDefinition.setOptionHandler("empty", this);
ItemDefinition.setOptionHandler("empty dish", this);
ItemDefinition.setOptionHandler("empty bowl", this);
return this;
}
public enum EmptyItem{
POT_OF_FLOUR(1933, 1931, "You empty the contents of the pot onto the floor."),
POT_OF_CORNFLOUR(7468, 1931, "You empty the contents of the pot onto the floor."),
BONE_MEAL(4255, 1931, "You empty the pot of crushed bones."),
BUCKET_OF_SAND(1783, BUCKET, BUCKET_EMPTY_MSG),
BUCKET_OF_MILK(1927, BUCKET, BUCKET_EMPTY_MSG),
BUCKET_OF_WATER(1929, BUCKET, BUCKET_EMPTY_MSG),
BUCKET_OF_COMPOST(6032, BUCKET, "You empty the bucket of compost."),
BUCKET_OF_SUPERCOMPOST(6034, BUCKET, "You empty the bucket of supercompost."),
BUCKET_OF_SLIME(4286, BUCKET, "You empty the contents of the bucket on the floor."),
VIAL_OF_WATER(227, VIAL, "You empty the vial."),
BOWL_OF_WATER(1921, 1923, "You empty the contents of the bowl onto the floor."),
JUG_OF_WATER(1937, 1935, "You empty the contents of the jug onto the floor."),
BURNT_PIE(2329, 2313, "You empty the pie dish."),
POTION(91, VIAL,null),
BURNT_STEW(2005, 1923, "You empty the contents of the bowl onto the floor."),
NETTLE_TEA(4239, 1923, "You empty the contents of the bowl onto the floor."),
NETTLE_WATER(4237, 1923, "You empty the contents of the bowl onto the floor."),
NETTLE_TEA_MILKY(4240, 1923, "You empty the contents of the bowl onto the floor."),
BURNT_CURRY(2013, 1923, "You empty the contents of the bowl onto the floor."),
BURNT_GNOMEBOWL(2175, 2166, "You empty the contents of the gnomebowl onto the floor."),
BURNT_EGG(7090, 1923, "You empty the contents of the bowl onto the floor."),
BURNT_ONION(7092, 1923, "You empty the contents of the bowl onto the floor."),
BURNT_MUSHROOM(7094, 1923, "You empty the contents of the bowl onto the floor.");
int fullId, emptyId;
String emptyMessage;
EmptyItem(int fullId, int emptyId, String emptyMessage){
this.fullId = fullId;
this.emptyId = emptyId;
this.emptyMessage = emptyMessage;
}
public static HashMap<Integer,Integer> emptyItemMap = new HashMap<>();
public static HashMap<Integer,String> emptyMessageMap = new HashMap<>();
static{
for(EmptyItem item : EmptyItem.values()){
emptyItemMap.putIfAbsent(item.fullId,item.emptyId);
emptyMessageMap.putIfAbsent(item.fullId,item.emptyMessage);
}
}
public static Item getEmpty(int id){
return new Item(emptyItemMap.get(id));
}
public static String getEmptyMessage(int id){
return emptyMessageMap.get(id);
}
}
}
+3
View File
@@ -305,5 +305,8 @@ class ServerConstants {
@JvmField
var BETTER_AGILITY_PYRAMID_GP = true
@JvmField
var BETTER_DFS = true
}
}
@@ -159,6 +159,7 @@ object ServerConfigParser {
ServerConstants.GRAFANA_PATH = data.getPath("integrations.grafana_log_path")
ServerConstants.GRAFANA_LOGGING = data.getBoolean("integrations.grafana_logging", false)
ServerConstants.GRAFANA_TTL_DAYS = data.getLong("integrations.grafana_log_ttl_days", 7L).toInt()
ServerConstants.BETTER_DFS = data.getBoolean("world.better_dfs", true)
val logLevel = data.getString("server.log_level", "VERBOSE").uppercase()
ServerConstants.LOG_LEVEL = parseEnumEntry<LogLevel>(logLevel) ?: LogLevel.VERBOSE
+2
View File
@@ -93,6 +93,8 @@ revenant_population = 30
i_want_to_cheat = false
#better agility pyramid gp reward (gp reward = 1000 + ((agility level / 99) * 9000))
better_agility_pyramid_gp = true
#better dragonfire shield attack (30 second cooldown instead of 2 minutes)
better_dfs = true
[paths]
#path to the data folder, which contains the cache subfolder and such