mirror of
https://gitlab.com/2009scape/2009scape.git
synced 2025-12-09 16:45:44 -07:00
Refactored equipment.add, fixes bug #420 (blaze it)
This commit is contained in:
parent
9ab9885eef
commit
8835799230
2 changed files with 280 additions and 59 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package core.game.container.impl;
|
package core.game.container.impl;
|
||||||
|
|
||||||
|
import api.EquipmentSlot;
|
||||||
import core.game.container.Container;
|
import core.game.container.Container;
|
||||||
import core.game.container.ContainerEvent;
|
import core.game.container.ContainerEvent;
|
||||||
import core.game.container.ContainerListener;
|
import core.game.container.ContainerListener;
|
||||||
|
|
@ -13,9 +14,14 @@ import core.net.packet.context.ContainerContext;
|
||||||
import core.net.packet.out.ContainerPacket;
|
import core.net.packet.out.ContainerPacket;
|
||||||
import core.net.packet.out.WeightUpdate;
|
import core.net.packet.out.WeightUpdate;
|
||||||
import core.plugin.Plugin;
|
import core.plugin.Plugin;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import rs09.game.interaction.InteractionListeners;
|
||||||
import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks;
|
import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks;
|
||||||
|
import rs09.game.system.SystemLogger;
|
||||||
import rs09.game.system.config.ItemConfigParser;
|
import rs09.game.system.config.ItemConfigParser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the equipment container.
|
* Represents the equipment container.
|
||||||
* @author Emperor
|
* @author Emperor
|
||||||
|
|
@ -64,87 +70,174 @@ public final class EquipmentContainer extends Container {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an item to the equipment container.
|
* Adds an item to the equipment container.
|
||||||
* @param item The item to add.
|
* @param newItem The item to add.
|
||||||
* @param inventorySlot The inventory slot of the item.
|
* @param inventorySlot The inventory slot of the item.
|
||||||
* @param fire If we should refresh.
|
* @param fire If we should refresh.
|
||||||
* @param fromInventory If the item is being equipped from the inventory.
|
* @param fromInventory If the item is being equipped from the inventory.
|
||||||
* @return {@code True} if succesful, {@code false} if not.
|
* @return {@code True} if succesful, {@code false} if not.
|
||||||
*/
|
*/
|
||||||
public boolean add(Item item, int inventorySlot, boolean fire, boolean fromInventory) {
|
public boolean add(Item newItem, int inventorySlot, boolean fire, boolean fromInventory) {
|
||||||
int slot = item.getDefinition().getConfiguration(ItemConfigParser.EQUIP_SLOT, -1);
|
int equipmentSlot = newItem.getDefinition().getConfiguration(ItemConfigParser.EQUIP_SLOT, -1);
|
||||||
if (slot == -1 && item.getDefinition().getConfiguration(ItemConfigParser.WEAPON_INTERFACE, -1) != -1) {
|
if (!isEquippable(newItem, equipmentSlot)) return false;
|
||||||
slot = SLOT_WEAPON;
|
|
||||||
}
|
ArrayList<Item> itemsToRemove = new ArrayList<>();
|
||||||
// slot = 3;
|
|
||||||
if (slot < 0) {
|
Item currentItem = super.get(equipmentSlot);
|
||||||
return false; // Item can't be equipped.
|
if(currentItem != null) itemsToRemove.add(currentItem);
|
||||||
}
|
|
||||||
if (!item.getDefinition().hasRequirement(player, true, true)) {
|
Item secondaryEquip = getSecondaryEquipIfApplicable(newItem, equipmentSlot);
|
||||||
|
if(secondaryEquip != null) itemsToRemove.add(secondaryEquip);
|
||||||
|
|
||||||
|
if(fromInventory && !player.getInventory().remove(newItem, inventorySlot, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Item current = super.get(slot);
|
|
||||||
if (current != null && current.getId() == item.getId() && current.getDefinition().isStackable()) {
|
if (!itemsToRemove.isEmpty()) {
|
||||||
|
ArrayList<Item> invalidatedEntries = new ArrayList<>();
|
||||||
|
for(Item current : itemsToRemove) {
|
||||||
|
if(current.getId() == newItem.getId() && current.getDefinition().isStackable()) {
|
||||||
|
addStackableItemToExistingStack(newItem, fromInventory, equipmentSlot, current);
|
||||||
|
invalidatedEntries.add(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemsToRemove.removeAll(invalidatedEntries);
|
||||||
|
|
||||||
|
if(itemsToRemove.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean successfullyRemovedAll = tryUnequipCurrent(itemsToRemove, newItem);
|
||||||
|
|
||||||
|
if(!successfullyRemovedAll) {
|
||||||
|
if (fromInventory) player.getInventory().add(newItem); //add the item back in case we weren't able to remove the currently equipped item(s)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.replace(newItem, equipmentSlot, fire);
|
||||||
|
setWeaponInterfaceWeaponName(newItem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEquippable(Item newItem, int slot) {
|
||||||
|
if (slot < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return newItem.getDefinition().hasRequirement(player, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setWeaponInterfaceWeaponName(Item newItem) {
|
||||||
|
if (newItem.getSlot() == SLOT_WEAPON) {
|
||||||
|
player.getPacketDispatch().sendString(newItem.getName(), 92, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryUnequipCurrent(ArrayList<Item> current, Item newItem) {
|
||||||
|
if(current.isEmpty()) return true;
|
||||||
|
int freeSlots = player.getInventory().freeSlots();
|
||||||
|
int neededSlots = getNeededSlotsToUnequip(current);
|
||||||
|
|
||||||
|
boolean hasSpaceForUnequippedItems = freeSlots >= neededSlots;
|
||||||
|
|
||||||
|
if(!hasSpaceForUnequippedItems) {
|
||||||
|
player.getPacketDispatch().sendMessage("Not enough space in your inventory!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean listenersSayWeCanUnequip = runUnequipHooks(current, newItem);
|
||||||
|
|
||||||
|
boolean allRemoved = true;
|
||||||
|
for(Item item : current) {
|
||||||
|
if(!remove(item)) {
|
||||||
|
allRemoved = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allAdded = allRemoved;
|
||||||
|
if(allRemoved) {
|
||||||
|
for (Item item : current) {
|
||||||
|
if (!player.getInventory().add(item)) {
|
||||||
|
allAdded = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listenersSayWeCanUnequip && allRemoved && allAdded) return true;
|
||||||
|
else {
|
||||||
|
//put things back if we couldn't remove everything
|
||||||
|
for(Item item : current) {
|
||||||
|
if(!containsItem(item)) {
|
||||||
|
add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNeededSlotsToUnequip(ArrayList<Item> current) {
|
||||||
|
int neededSlots = 0;
|
||||||
|
|
||||||
|
for(Item item : current) {
|
||||||
|
if(!item.getDefinition().isStackable()) {
|
||||||
|
neededSlots++;
|
||||||
|
} else {
|
||||||
|
if(player.getInventory().getAmount(item.getId()) == 0) {
|
||||||
|
neededSlots++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return neededSlots;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Item getSecondaryEquipIfApplicable(Item newItem, int equipmentSlot) {
|
||||||
|
Item secondaryEquipItem = null;
|
||||||
|
if (newItem.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) {
|
||||||
|
secondaryEquipItem = get(SLOT_SHIELD);
|
||||||
|
} else if (equipmentSlot == SLOT_SHIELD) {
|
||||||
|
Item inSlot = player.getEquipment().get(SLOT_WEAPON);
|
||||||
|
if(inSlot != null && inSlot.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false))
|
||||||
|
secondaryEquipItem = get(SLOT_WEAPON);
|
||||||
|
}
|
||||||
|
return secondaryEquipItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean runUnequipHooks(ArrayList<Item> currentItems, Item newItem) {
|
||||||
|
boolean canContinue = true;
|
||||||
|
|
||||||
|
for(Item currentItem : currentItems) {
|
||||||
|
Plugin<Object> plugin = currentItem.getDefinition().getConfiguration("equipment", null);
|
||||||
|
if (plugin != null) {
|
||||||
|
Object object = plugin.fireEvent("unequip", player, currentItem, newItem);
|
||||||
|
if (object != null && !((Boolean) object)) {
|
||||||
|
canContinue = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canContinue = InteractionListeners.run(currentItem.getId(), player, currentItem, false);
|
||||||
|
|
||||||
|
if(!canContinue) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return canContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStackableItemToExistingStack(Item item, boolean fromInventory, int slot, Item current) {
|
||||||
int amount = getMaximumAdd(item);
|
int amount = getMaximumAdd(item);
|
||||||
if (item.getAmount() > amount) {
|
if (item.getAmount() > amount) {
|
||||||
amount += current.getAmount();
|
amount += current.getAmount();
|
||||||
} else {
|
} else {
|
||||||
amount = current.getAmount() + item.getAmount();
|
amount = current.getAmount() + item.getAmount();
|
||||||
}
|
}
|
||||||
|
Item transferItem = new Item(current.getId(), amount);
|
||||||
if (fromInventory) {
|
if (fromInventory) {
|
||||||
player.getInventory().remove(new Item(item.getId(), amount - current.getAmount()));
|
player.getInventory().remove(transferItem);
|
||||||
}
|
}
|
||||||
replace(new Item(item.getId(), amount), slot);
|
replace(transferItem, slot);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (fromInventory && current != null) {
|
|
||||||
Plugin<Object> plugin = current.getDefinition().getConfiguration("equipment", null);
|
|
||||||
if (plugin != null) {
|
|
||||||
Object object = plugin.fireEvent("unequip", player, current, item);
|
|
||||||
if (object != null && !((Boolean) object)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fromInventory && !player.getInventory().remove(item, inventorySlot, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Item secondary = null;
|
|
||||||
if (item.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) {
|
|
||||||
secondary = get(SLOT_SHIELD);
|
|
||||||
} else if (slot == SLOT_SHIELD) {
|
|
||||||
secondary = get(SLOT_WEAPON);
|
|
||||||
if (secondary != null && !secondary.getDefinition().getConfiguration(ItemConfigParser.TWO_HANDED, false)) {
|
|
||||||
secondary = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int currentSlot = -1;
|
|
||||||
if (current != null) {
|
|
||||||
currentSlot = inventorySlot;
|
|
||||||
if (current.getDefinition().isStackable() && player.getInventory().contains(current.getId(), 1)) {
|
|
||||||
currentSlot = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (current != null && !player.getInventory().add(current, true, inventorySlot)) {
|
|
||||||
player.getInventory().add(item);
|
|
||||||
player.getPacketDispatch().sendMessage("Not enough space in your inventory!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (secondary != null && !player.getInventory().add(secondary)) {
|
|
||||||
if (current != null && currentSlot != -1) {
|
|
||||||
player.getInventory().remove(current, currentSlot, false);
|
|
||||||
}
|
|
||||||
player.getInventory().add(item);
|
|
||||||
player.getPacketDispatch().sendMessage("Not enough space in your inventory!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
super.replace(item, slot, fire);
|
|
||||||
if (item.getSlot() == SLOT_WEAPON) {
|
|
||||||
player.getPacketDispatch().sendString(item.getName(), 92, 0);
|
|
||||||
}
|
|
||||||
if (secondary != null) {
|
|
||||||
super.remove(secondary);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
128
Server/src/test/kotlin/content/EquipTests.kt
Normal file
128
Server/src/test/kotlin/content/EquipTests.kt
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
package content
|
||||||
|
|
||||||
|
import TestUtils
|
||||||
|
import api.EquipmentSlot
|
||||||
|
import core.game.node.entity.skill.Skills
|
||||||
|
import core.game.node.item.Item
|
||||||
|
import org.junit.jupiter.api.Assertions
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.rs09.consts.Items
|
||||||
|
import rs09.game.content.global.action.EquipHandler
|
||||||
|
import rs09.game.interaction.InteractionListener
|
||||||
|
import rs09.game.interaction.InteractionListeners
|
||||||
|
|
||||||
|
class EquipTests {
|
||||||
|
companion object {
|
||||||
|
init {TestUtils.preTestSetup(); EquipHandler().defineListeners()}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equipShouldFireEquipListeners() {
|
||||||
|
var didRun = false
|
||||||
|
val listener = object : InteractionListener {
|
||||||
|
override fun defineListeners() {
|
||||||
|
onEquip(4151) {_,_ -> didRun = true; return@onEquip true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.defineListeners()
|
||||||
|
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.inventory.add(Item(4151))
|
||||||
|
InteractionListeners.run(4151, InteractionListener.ITEM, "equip", p, p.inventory[0])
|
||||||
|
|
||||||
|
Assertions.assertEquals(true, didRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun unequipShouldFireUnequipListeners() {
|
||||||
|
var didRun = false
|
||||||
|
val listener = object : InteractionListener {
|
||||||
|
override fun defineListeners() {
|
||||||
|
onUnequip(4151) {_,_ -> didRun = true; return@onUnequip true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.defineListeners()
|
||||||
|
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.equipment.replace(Item(4151), EquipmentSlot.WEAPON.ordinal)
|
||||||
|
EquipHandler.unequip(p, EquipmentSlot.WEAPON.ordinal, 4151)
|
||||||
|
|
||||||
|
Assertions.assertEquals(true, didRun)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equippingItemThatReplacesAnotherItemShouldCallUnequipListenersForTheReplacedItem() {
|
||||||
|
var didRun = false
|
||||||
|
val listener = object : InteractionListener {
|
||||||
|
override fun defineListeners() {
|
||||||
|
onUnequip(4151) {_,_ -> didRun = true; return@onUnequip true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
listener.defineListeners()
|
||||||
|
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.equipment.replace(Item(4151), EquipmentSlot.WEAPON.ordinal)
|
||||||
|
p.inventory.add(Item(1333))
|
||||||
|
p.skills.staticLevels[Skills.ATTACK] = 40
|
||||||
|
InteractionListeners.run(1333, InteractionListener.ITEM, "equip", p, p.inventory[0])
|
||||||
|
|
||||||
|
Assertions.assertEquals(true, didRun, p.equipment.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equippingItemShouldAddUnequippedItemToExistingStackInInventory() {
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.skills.staticLevels[Skills.ATTACK] = 99
|
||||||
|
p.equipment.replace(Item(Items.BRONZE_DART_806, 1000), EquipmentSlot.WEAPON.ordinal)
|
||||||
|
p.inventory.add(Item(Items.BRONZE_DART_806, 1000))
|
||||||
|
p.inventory.add(Item(Items.RUNE_SCIMITAR_1333))
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.RUNE_SCIMITAR_1333, InteractionListener.ITEM, "equip", p, p.inventory[1])
|
||||||
|
Assertions.assertEquals(2000, p.inventory.getAmount(Items.BRONZE_DART_806), "\n" + p.inventory.toString() + "\n" + p.equipment.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equippingItemThatUnequipsTwoItemsShouldBeAllowedWithOnlyOneInitiallyFreeSlot() {
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.equipment.replace(Item(Items.BRONZE_SWORD_1277), EquipmentSlot.WEAPON.ordinal)
|
||||||
|
p.equipment.replace(Item(Items.WOODEN_SHIELD_1171), EquipmentSlot.SHIELD.ordinal)
|
||||||
|
|
||||||
|
p.inventory.add(Item(Items.CUP_OF_TEA_1978, 26))
|
||||||
|
p.inventory.add(Item(Items.BRONZE_2H_SWORD_1307))
|
||||||
|
|
||||||
|
Assertions.assertEquals(1, p.inventory.freeSlots())
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.BRONZE_2H_SWORD_1307, InteractionListener.ITEM, "equip", p, p.inventory[26])
|
||||||
|
Assertions.assertEquals(0, p.inventory.freeSlots())
|
||||||
|
Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.inventory[26].id)
|
||||||
|
Assertions.assertEquals(Items.WOODEN_SHIELD_1171, p.inventory[27].id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun shouldNotBeAbleToEquipA2HWeaponAndAShieldAtTheSameTime() {
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.inventory.add(Item(Items.BRONZE_2H_SWORD_1307))
|
||||||
|
p.inventory.add(Item(Items.WOODEN_SHIELD_1171))
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.BRONZE_2H_SWORD_1307, InteractionListener.ITEM, "equip", p, p.inventory[0])
|
||||||
|
Assertions.assertEquals(Items.BRONZE_2H_SWORD_1307, p.equipment.get(EquipmentSlot.WEAPON.ordinal).id)
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.WOODEN_SHIELD_1171, InteractionListener.ITEM, "equip", p, p.inventory[1])
|
||||||
|
Assertions.assertEquals(Items.BRONZE_2H_SWORD_1307, p.inventory[0]?.id ?: -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equippingShieldShouldNotUnequipOneHandedWeapon() {
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.inventory.add(Item(Items.BRONZE_SWORD_1277))
|
||||||
|
p.inventory.add(Item(Items.WOODEN_SHIELD_1171))
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.BRONZE_SWORD_1277, InteractionListener.ITEM, "equip", p, p.inventory[0])
|
||||||
|
Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.equipment[EquipmentSlot.WEAPON.ordinal].id)
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.WOODEN_SHIELD_1171, InteractionListener.ITEM, "equip", p, p.inventory[1])
|
||||||
|
Assertions.assertEquals(Items.BRONZE_SWORD_1277, p.equipment[EquipmentSlot.WEAPON.ordinal]?.id ?: -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun equippingStackableItemShouldAddToExistingStackInEquipmentIfApplicable() {
|
||||||
|
val p = TestUtils.getMockPlayer("bill")
|
||||||
|
p.equipment.replace(Item(Items.BRONZE_ARROW_882, 100), EquipmentSlot.AMMO.ordinal)
|
||||||
|
p.inventory.add(Item(Items.BRONZE_ARROW_882, 200))
|
||||||
|
|
||||||
|
InteractionListeners.run(Items.BRONZE_ARROW_882, InteractionListener.ITEM, "equip", p, p.inventory[0])
|
||||||
|
Assertions.assertEquals(300, p.equipment[EquipmentSlot.AMMO.ordinal].amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue