How do I remove an item from my inventory?

Hi all. I’m trying to remove items from an inventory as you use them items, and I’m having some trouble understanding how to get it working.

class Inventory {
  constructor() {
    this.items = [
      { name: "Potion", quantity: 2 },
      { name: "Remedy", quantity: 3 }
    ];
    // this.addItem({ name: "Potion", quantity: 10 });
  }

  addItem(item) {
    let existingKey = Object.keys(this.items).find(
      (key) => this.items[key].name === item.name
    );

    if (existingKey) {
      console.log("key exists");
      this.items[existingKey].quantity += item.quantity;
    } else {
      this.items.push(item);
    }
  }
  
  removeItem(item) {
    let existingKey = Object.keys(this.items).find(
      (key) => this.items[key].name === item.name
    );

    if (existingKey) {
      console.log("key exists");
      this.items[existingKey].quantity -= item.quantity;
    } else {
      // something here to delete the item?
    }
  }
}

let InventoryScene = new Phaser.Class({
  Extends: Phaser.Scene,
  initialize: function () {
    Phaser.Scene.call(this, { key: "InventoryScene" });
  },
  init: function (data) {
    this.inventory = data.inventory.inventory;
  },
  preload: function () {
    this.load.scenePlugin({
      key: "rexuiplugin",
      url: url,
      sceneKey: "rexUI"
    });
  },
  refresh: function () {
      this.list = createList(
      this,
      800 / 2,
      60,
      this.inventory.items,
      (button) => {
        let data = { button: button };
        this.scene.pause();
        this.scene.launch("UseMenuScene", data);
      },
      () => {
        // console.log("outt!!!")
        console.log(this.pointer)
      }
    );
  },
  create: function () {
    this.menuKeys = this.input.keyboard.addKeys("M");
    this.text = this.add
      .text(800 / 2, 20, "inventory scene")
      .setOrigin(0.5, 0.5);

    let scene = this;

    console.log(this.inventory.items);
    
    this.pointer = this.input.activePointer

    this.refresh()
  },
  update: function () {
    if (Phaser.Input.Keyboard.JustDown(this.menuKeys.M)) {
      this.scene.wake("InGameMenuScene");
      this.scene.resume("InGameMenuScene");
      this.scene.sleep();
    }
  }
});
let UseMenuScene = new Phaser.Class({
  Extends: Phaser.Scene,
  initialize: function () {
    Phaser.Scene.call(this, { key: "UseMenuScene" });
  },
  init: function (data) {
    this.itemPressed = data.button;
    this.isTouching = false
  },
  preload: function () {
    this.load.scenePlugin({
      key: "rexuiplugin",
      url: url,
      sceneKey: "rexUI"
    });
  },
  create: function () {
    this.menuKeys = this.input.keyboard.addKeys("M");
    
    this.pointer = this.input.activePointer
    
    let items = [{ name: "Use" }, { name: "Equip" }];
    this.menu = createUseMenu(this, this.itemPressed.x, this.itemPressed.y, items)
    
    this.menu.on("button.over", (button) => {
      button.getElement("background").setStrokeStyle(2, 0xffffff);
      this.isTouching = true
    })
    this.menu.on("button.out", (button) => {
      button.getElement("background").setStrokeStyle();
      this.isTouching = false
    })
    this.menu.on("button.click", (button) => {
      this.isTouching = false
      if (this.itemPressed.childrenMap.text._text == "Potion") {
        if (button.childrenMap.text._text == "Use") {
          
          let gameUIScene = this.scene.get("GameUIScene")
          gameUIScene.usePotion()
          
          let inventoryScene = this.scene.get("InventoryScene")
          console.log("needs to be destroyed")
          inventoryScene.inventory.removeItem("Potion")
          inventoryScene.refresh()
            
          this.isTouching = false
          this.scene.resume("InventoryScene")
          this.scene.sleep()
          this.menu.destroy()
        } 
      }
    })
  },
  update: function () {
    if (this.isTouching == false) {
      this.input.on("pointerdown", () => {
        this.scene.resume("InventoryScene")
        this.scene.sleep()
        this.menu.destroy()
      })
    }
    if (this.isTouching == true) {
      this.input.off("pointerdown")
    }
    if (Phaser.Input.Keyboard.JustDown(this.menuKeys.M)) {
      this.menu.destroy();
      this.scene.resume("InventoryScene");
      this.scene.sleep();
    }
  }
});

I can decrease the quantity of my items inside the click event of the use button by calling the inventory items, but I would assume it would be done in the Inventory class itself. So I made a removeItem function in the inventory class. I already had a addItem function which worked so I used the same code. I had attempted this a few hours earlier but I ran into a problem. I could remove the item from the inventory, but it would not destroy itself in the ui.

I’ve tried it again in codepen, and I’ve gone further away from solving the problem now that I can’t delete it from the inventory but also remove the button graphic that I’m using. This is using the RexRainbow ui plugins incase anyone was wondering. Heres the codepen here

My understanding of model and view concepts applying them in a practical way in JS has been shaky at best and has gotten me more than a bit confused. If anyone could suggest me any help or advice, or some critiquing of my knowledge of any of this, it would help heaps and would be much appreciate it. :slight_smile:

A bit cryptic :slight_smile:

I guess if removeItem(item) is called, item MUST exist, so checking if (existingKey) makes no sense.
You could check for quantity:

if (this.items[existingKey].quantity > 0) this.items[existingKey].quantity -= item.quantity;
else this.items.splice(existingKey, 1);

Also it would be easier to use a key/value dictionary with item.name as key, so

this.items = {}
this.items['Potion'] = { quantity : 2 };
this.items['Remedy'] = { quantity : 3 };

You wouldn’t need to keep iterating an array just to find a key…

Right. I hear you. I gave this a go, and quantity decrease is working, I’m still unsure how to refresh the inventory properly enough for the graphic to dissapear. For the items, I figured your method would help so I gave it a whirl. The list displaying the items takes in an array of objects. I’m unsure how the list of buttons could display the items in the configuration that you suggest. Thanks for your suggestions.

Object.keys(this.inventory.items) returns the names as an array.
To remove from the dictionary, use delete this.items['Potion']; since splice only works for arrays.

The list has to take in an array objects like [{name: "Potion"}, {name: "Remedy"}] for it to display using the rex ui plugins. I can definitely use the object.keys function as you mentioned, but I would end up abandoning the plugins, which I am open to but I am curious to know of a solution.

Object.keys(this.inventory.items).map(name => {
            return { "name": name }
        });

Okay. So the refresh function in the inventory scene is as follows:

refresh() {
  let items = Object.keys(this.player.inventory.items).map(name => {
    return {"name": name, "quantity": this.player.inventory.items[name].quantity}
  })

this.list = createList(this, 640 / 2, 480 /2, items, (button) => {
    this.scene.pause()
    let dataForUseMenu = {button: button, player: this.player}
    this.scene.launch("UseMenuScene", dataForUseMenu)
  })
},

and the removeItem function is as follows:

removeItem(item) {
  if (this.items[item].quantity > 1) {
    this.items[item].quantity = this.items[item].quantity - 1;
  } else {
    delete this.items[item]
  }    
}

Its removing from the inventory but it isnt updating the list properly. I see how much easier it is to handle items in that format. Because I’m using the rex ui plugins, removing the text of the item has made things harder to understand. There is some funny behaviour when removing the last potion. Logging the console shows the item has left the inventory, but the text on screen does not dissapear instead it does something else. Heres my codepen of what I’m refering to. Once again, thanks for your suggestions. It’s helped me understand an approach to code that I wouldn’t have otherwise thought.

I know nothing about @rexrainbow plugins :slight_smile:
Maybe open a new topic about how his menus update. And be sure to mention his handle.

I couldn’t see where is the code about rexUI, in first post of this thread.

@rexrainbow So in the code I have a function that creates the list like so:

let createList = function (scene, x, y, items, onClick) {
  let list = scene.rexUI.add.menu({
    x: x,
    y: y,
    items: items,
    createButtonCallback: function (item, i) {
      return scene.rexUI.add.label({
        background: scene.rexUI.add.roundRectangle(
          0,
          0,
          2,
          2,
          0,
          COLOR_PRIMARY
        ),
        text: scene.add.text(0, 0, item.name, {
          fontSize: "15px"
        })
      });
    }
  });
  let quantityList = scene.rexUI.add.menu({
    x: x + 80,
    y: y,
    items: items,
    createButtonCallback: function (item, i) {
      return scene.rexUI.add.label({
        background: scene.rexUI.add.roundRectangle(
          0,
          0,
          2,
          2,
          0,
          COLOR_PRIMARY
        ),
        text: scene.add.text(0, 0, item.quantity, {
          fontSize: "15px"
        })
      });
    }
  });
  
  list
    .on("button.over", function (button) {
      button.getElement("background").setStrokeStyle(2, 0xffffff);
    })
    .on("button.out", function (button) {
      button.getElement("background").setStrokeStyle();
    })
    .on("button.click", function (button) {
      onClick(button);
    });
  return list;
};

At which I am then calling the createList variable in a refresh function.

refresh: function () {
    this.items = Object.keys(this.player.inventory.items).map(name => {
      return {"name": name, "quantity": this.player.inventory.items[name].quantity}
    })

    this.list = createList(this, 640 / 2, 480 /2, this.items, (button) => {
      this.scene.pause()
      let dataForUseMenu = {button: button, player: this.player}
      
      this.scene.launch("UseMenuScene", dataForUseMenu)
    }) 

When you use the potion, it is removed once the quantity has depleted. However, the buttons itself graphically are not removed, which is what I intended and can’t figure out how to change the list of buttons to be the same as the inventory after an item is deleted.

As in my codepen example here, after the potion is deleted, the button slot does something strange. To be honest, I’m unsure if these plugins were designed for such purposes, but I was curious and gave it a try with some help.

For items or inventory, please try this Grid table (live demo), and there are methods of changing items list.

Okay :). I’ll take a look into it. I’ll post back here about my progress. Thanks again.

A quick look at your codepen, you only keep creating new menus, so they display over each other. You need to destroy the old menu. Or change the existing one,

Hmm. I added the grid table and it seems to work on my local server, but when I tried to implement it in codepen, it works except that a slot remains whereas in my local server, it is removed. The code in codepen is not that much different other than an extra scene that handles an extra menu. I’m not sure why that is happening, unless I’m missing something.

@rexrainbow

The only other thing that I’m confused about, is adding extra text for quantity to the item slot. I’m unsure how to add extra text to the item to display the quantity.

Try this overlap-sizer, to place another text game object above item image, in cell container.

Here is a demo of my new Badge Label component, which extended from overlap-sizer.