- Mod allows bank to go over capacity if items which have placeholders are added.
- "Sell all unlocked items in Tab" shows a count that includes all placeholders in the tab.
When an item is received from a skill or from combat loot, the Bank.hasItem() method is not used to detect whether there is room in the bank for the new item. Instead, the Bank.addItem() method is called directly from Rewards and CombatLoot objects.
The Bank.addItem() method checks bankItem !== undefined first to determine whether there is an existing slot that a newly looted item can go into. If this returns true, the bank assumes there is room for the item, since it should only increase the quantity of the item already occupying a bank slot.
However, when a placeholder exists for an item, bankItem !== undefined still returns true. This is because an item with a placeholder still has an entry in the Bank.items map, only its quantity is set to 0. As long as this condition returns true, the bank carries on adding the item whether it has space or not, skipping over the free space check (this.occupiedSlots < this.maximumSlots).
While this could be fixed at the "skill" level, patching the Rewards.giveRewards() and CombatLoot.lootItem() methods (manually bypassing the addItem() call when the quantity of the existing item in the map is 0), this would require a re-implementation of the rest of the addItem() functionality. Thus, patching addItem() itself seems to be the most straightforward and centralized approach.
/**
* @description Adds the given quantity of the item to the bank
* @param item The item to add
* @param quantity The quantity of the item to add. Must be positive.
* @param logLost If the item should be logged as lost if it does not fit
* @param found If the item should contribute to item statistics
* @param ignoreSpace If the item should ignore conventional bank space limits
* @param notify Whether to show a notification when the item is added
* @param itemSource The source of the item, where it came from
* @returns True if the item was successfully added to the bank
*/
addItem(item, quantity, logLost, found, ignoreSpace = false, notify = true, itemSource = 'Game.Unknown') {
var _a, _b;
if (quantity <= 0)
throw new Error(`Tried to add negative or zero quantity to bank.`);
let success = false;
let bankItem = this.items.get(item);
let oldQuantity = 0;
let newQuantity = quantity;
if (bankItem !== undefined) {
oldQuantity = bankItem.quantity;
bankItem.quantity += quantity;
newQuantity = bankItem.quantity;
success = true;
}
else if (this.occupiedSlots < this.maximumSlots || ignoreSpace) {
const tab = this.getItemDefaultTab(item);
bankItem = new BankItem(this, item, quantity, tab, this.itemsByTab[tab].length);
this.items.set(item, bankItem);
this.itemsByTab[tab].push(bankItem);
if (this.game.settings.bankSortOrder === 5 /* BankSortOrderSetting.Custom */ && !this.customSortOrder.includes(item))
this.storeCustomSortOrder();
success = true;
this.renderQueue.bankSearch = true;
this.newItemsAdded = true;
if (bankItem.tabPosition === 0)
this.renderQueue.tabIcons.add(tab);
}
if (success) {
if (found) {
const newItem = this.game.stats.itemFindCount(item) === 0;
this.game.stats.Items.add(item, ItemStats.TimesFound, quantity);
if (newItem) {
const event = new ItemFoundEvent(item);
this._events.emit('itemFound', event);
if (this.emitItemEvents)
item.emit('found', event);
this.game.completion.updateItem(item);
this.glowingItems.add(item);
if (item instanceof EquipmentItem)
this.game.minibar.addItemOnDiscovery(item);
this.game.renderQueue.birthdayEventProgress = true;
}
}
if (!loadingOfflineProgress)
this.game.telemetry.createItemGainedEvent(item, quantity, itemSource);
this.renderQueue.items.add(item);
(_a = this.game.archaeology) === null || _a === void 0 ? void 0 : _a.renderQueue.museumArtefacts.add(item);
this.queueQuantityUpdates(item);
if (notify)
this.game.combat.notifications.add({
type: 'Item',
args: [item, quantity],
});
const event = new ItemQuantityChangedEvent(item, oldQuantity, newQuantity);
this._events.emit('itemChanged', event);
if (this.emitItemEvents)
item.emit('bankQuantityChanged', event);
}
else {
if (notify)
this.game.combat.notifications.add({
type: 'BankFull',
args: [],
});
if (logLost) {
this.lostItems.set(item, ((_b = this.lostItems.get(item)) !== null && _b !== void 0 ? _b : 0) + quantity);
}
}
return success;
} giveRewards(ignoreBankSpace = false) {
let notAllItemsGiven = false;
this._items.forEach((quantity, item) => {
notAllItemsGiven =
!this.game.bank.addItem(item, quantity, true, true, ignoreBankSpace, true, this.source) || notAllItemsGiven;
});
this._currencies.forEach((quantity, currency) => {
currency.add(quantity);
if (currency === this.game.gp)
this.game.telemetry.createGPAdjustedEvent(quantity, currency.amount, this.source);
if (currency === this.game.abyssalPieces)
this.game.telemetry.createAPAdjustedEvent(quantity, currency.amount, this.source);
});
this._xp.forEach((xp, skill) => {
const xpBefore = skill.xp;
const levelBefore = skill.level;
if (xp.noAction > 0)
skill.addXP(xp.noAction);
xp.action.forEach((amount, action) => {
skill.addXP(amount, action);
});
if (skill.xp > xpBefore) {
this.game.telemetry.createOnlineXPGainEvent(skill, this.actionInterval, xpBefore, skill.xp, levelBefore, skill.level);
}
});
this._abyssalXP.forEach((xp, skill) => {
const xpBefore = skill.abyssalXP;
const levelBefore = skill.abyssalLevel;
if (xp.noAction > 0)
skill.addAbyssalXP(xp.noAction);
xp.action.forEach((amount, action) => {
skill.addAbyssalXP(amount, action);
});
if (skill.abyssalXP > xpBefore) {
this.game.telemetry.createOnlineAXPGainEvent(skill, this.actionInterval, xpBefore, skill.abyssalXP, levelBefore, skill.abyssalLevel);
}
});
return notAllItemsGiven;
} lootItem(id) {
const drop = this.drops[id];
if (this.game.bank.addItem(drop.item, drop.quantity, false, true, false, true, 'Other.CombatLootContainer')) {
this.game.stats.Combat.add(CombatStats.ItemsLooted, drop.quantity);
this.drops.splice(id, 1);
this.renderRequired = true;
this.render();
}
else {
notifyPlayer(this.game.attack, getLangString('TOASTS_NO_BANK_ROOM'), 'danger');
}
} lootAll() {
this.drops = this.drops.filter((drop) => {
const fit = this.game.bank.addItem(drop.item, drop.quantity, false, true, false, true, 'Other.CombatLootContainer');
if (fit)
this.game.stats.Combat.add(CombatStats.ItemsLooted, drop.quantity);
return !fit;
});
if (this.drops.length > 0)
notifyPlayer(this.game.attack, getLangString('TOASTS_NO_BANK_ROOM_EVERYTHING'), 'danger');
this.renderRequired = true;
this.render();
}