Show image on item selection

Discussion in 'Javascript/Plugin Support' started by Jimminybob, Mar 20, 2018.

  1. Jimminybob

    Jimminybob Villager Member

    Messages:
    24
    Likes Received:
    7
    First Language:
    English
    Primarily Uses:
    RMMV
    Hi all,

    In the item section of my custom menu I want to show an image related to the item that is currently selected, which would show a sketch of the item and a brief description (i'm making the menu look like it is all handwritten/drawn). Is such a thing possible?

    I was thinking of giving all the images some kind of naming convention, something like "Item_itemname" in the hopes that I can create a generic function where I can just pass the item name in and show the relevant image from the folder. If such a thing is possible can someone help me get it started?

    Thanks
     
    #1
  2. Aloe Guvner

    Aloe Guvner Walrus Veteran

    Messages:
    1,600
    Likes Received:
    1,005
    Location:
    USA
    First Language:
    English
    Primarily Uses:
    RMMV
    Yes, definitely possible.

    I'm not sure how much you know of Javascript/coding in MV (including how to alias functions) but I'll proceed under the assumption that you have some knowledge since you mentioned creating a custom menu.

    I'll start with some pseudo-code, play with it a bit and come back for questions, then I'll give you more details.

    Theory:
    1. Create a new window to hold the item picture and assign it as a property of the Item List Window.
      • Now you can modify the new window directly from the Item List Window
    2. Change the select() method of the Item List Window to update the new window.
    It does actually, it inherits the method from the Window_Selectable prototype. You can use this to your advantage.

    Pseudo-Code:

    • Create a new window that inherits from Window_Base (the basic stuff):
    Code:
    function Window_DrawTheItemPictures {
        this.initialize blah blah blah
    }
    
    Window_DrawTheItemPictures.prototype = Object.create(Window_Base);
    Window_DrawTheItemPictures.prototype.constructor = Window_DrawTheItemPictures;
    
    Window_DrawTheItemPictures.initialize = blah blah blah
    //x , y , width , height etc. etc.
    
    • Add the new window to the scene.
    • Add the new window as a property of the item list window.

    Code:
    alias the Scene_Item.prototype.create
    Scene_Item.prototype.create = function() {
        call the alias
        this._itemPicturesWindow = new Window_DrawTheItemPictures();
        this.addWindow(this._itemPicturesWindow); //now the window is a child of the scene
        this._itemWindow._itemPicturesWindow = this._itemPicturesWindow; // <-- !! Important!!
        //Now the pictures window is also a property of the item list window
        console.log(this._itemWindow._itemPicturesWindow === this._itemPicturesWindow); //true
        //it's not a copy of the window, it's literally the same window object. whatever you do to one is
        //done to the other
    };
    
    • Change the item list window select() method to pass the item to the new window

    Code:
    Window_ItemList.prototype.select = function(index) {
        Window_Selectable.prototype.select.call(this, index);
        this.refreshMyItemPictureWindow(this.item());
    };
    
    • Interact with the new item picture window from the item list window
    Code:
    Window_ItemList.prototype.refreshMyItemPictureWindow = function(item) {
        this._itemPictureWindow.setImageName(item);
    };
    
    • Grab the image name out of the item that is passed to the item picture window
    • Refresh the item picture window
    Code:
    Window_DrawTheItemPictures.prototype.setImageName = function(item) {
        this._imageName = item.name; //change to your liking
        this.refresh();
    };
    
    • Define the item picture window refresh
    Code:
    Window_DrawTheItemPictures.prototype.refresh = function() {
        if (this._imageName !== undefined) {
            var bitmap = ImageManager.loadPicture(this._imageName);
            var sx = 0; //x-coordinate in source picture
            var sy = 0; //y-coordinate in source picture
            var sw = 100; //width in source picture
            var sh = 100; //height in source picture
            var dx = 0; //x-coordinate in the destination window
            var dy = 0; //y-coordinate in the destination window
            this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy);
    };
    
    • Preload all the images
    Code:
    May or may not be necessary.
    In the Scene_Item create method, might be necessary to read the party's inventory and preload the images so that they can be drawn immediately.
    
     
    #2
    caethyril likes this.
  3. Jimminybob

    Jimminybob Villager Member

    Messages:
    24
    Likes Received:
    7
    First Language:
    English
    Primarily Uses:
    RMMV
    Hi,

    A huge thanks for your help, your descriptions and examples are really thorough. I've tried adding in the code you showed into my own but for some reason when I run it the whole custom menu I have built fails to load and the game reverts it back to the original stock menu. I'm not sure why this is though as I would have just expected it to cause an error if something was wrong.

    The section of code where I am building my item scene changes is below, would you be able to see where i've gone wrong?

    Code:
    function Window_DrawTheItemPictures () {
            this.initialize.apply(this, arguments);
        };
    
        Window_DrawTheItemPictures.prototype = Object.create(Window_Base.prototype);
        Window_DrawTheItemPictures.prototype.constructor = Window_DrawTheItemPictures;
    
        Window_DrawTheItemPictures.prototype.initialize = function(x, y){
            var width = 100;
            var height = 100;
            Window_Base.prototype.initialize.call(this, x, y, width, height);
            this.refresh();
        };
    
    
        Scene_Item.prototype.create = function() {
            Scene_ItemBase.prototype.create.call(this);
            this.createCategoryWindow();
            this.createHelpWindow();
            this.createItemWindow();
    
            this._itemPicturesWindow = new Window_DrawTheItemPictures();
            this.addWindow(this._itemPicturesWindow);
            this._itemWindow._itemPicturesWindow = this._itemPicturesWindow;
            
            console.log(this._itemWindow._itemPicturesWindow === this._itemPicturesWindow);
        };
            
        Scene_Item.prototype.createCategoryWindow = function() {
            this._categoryWindow = new Window_ItemCategory();
            this._categoryWindow.setHelpWindow(this._helpWindow);
            this._categoryWindow.x = 60;
            this._categoryWindow.y = 30;   
            this._categoryWindow.width = 350;
            this._categoryWindow.opacity = 0;
            this._categoryWindow.setHandler('ok',     this.onCategoryOk.bind(this));
            this._categoryWindow.setHandler('cancel', this.popScene.bind(this));
            this.addWindow(this._categoryWindow);
        };
            
        Window_ItemCategory.prototype.makeCommandList = function() {
            this.addCommand(TextManager.item,    'item');
            this.addCommand(TextManager.keyItem, 'keyItem');
        };
        
        Scene_Item.prototype.createItemWindow = function() {
            var wy = this._categoryWindow.y + this._categoryWindow.height;
            var wh = Graphics.boxHeight - wy - 20;
            this._itemWindow = new Window_ItemList(60, wy, 350, wh);
            this._itemWindow.opacity = 0;
            this._itemWindow.setHelpWindow(this._helpWindow);
            this._itemWindow.setHandler('ok',     this.onItemOk.bind(this));
            this._itemWindow.setHandler('cancel', this.onItemCancel.bind(this));
            this.addWindow(this._itemWindow);
            this._categoryWindow.setItemWindow(this._itemWindow);
        };
    
        Scene_ItemBase.prototype.useItem = function() {
            this.playSeForItem();
            this.user().useItem(this.item());
            this.applyItem();
            this.checkCommonEvent();
            this.checkGameover();
        };
        
        Window_ItemCategory.prototype.maxCols = function() {
            return 2;
        };
    
        Window_ItemList.prototype.maxCols = function() {
            return 1;
        };
    
        Window_ItemList.prototype.spacing = function() {
            return 24;
        };
    
    
        Window_ItemList.prototype.select = function(index) {
            Window_Selectable.prototype.select.call(this, index);
            this.refreshMyItemPictureWindow(this.item());
        };
    
        Window_ItemList.prototype.refreshMyItemPictureWindow = function(item) {
            this._itemPicturesWindow.setImageName(item);
        };
    
        Window_DrawTheItemPictures.prototype.setImageName = function(item) {
            this._imageName = item.name;
            this.refresh();
        };
    
        Window_DrawTheItemPictures.prototype.refresh = function() {
            if (this._imageName !== undefined) {
                var bitmap = ImageManager.loadPicture(this._imageName);
                var sx = 0; //x-coordinate in source picture
                var sy = 0; //y-coordinate in source picture
                var sw = 100; //width in source picture
                var sh = 100; //height in source picture
                var dx = 0; //x-coordinate in the destination window
                var dy = 0; //y-coordinate in the destination window
                this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy);
        };

    Also you mention about preloading images for performance reasons, how is this done? I don't remember seeing anything in the system about doing something like this.

    Thanks
     
    #3
  4. Aloe Guvner

    Aloe Guvner Walrus Veteran

    Messages:
    1,600
    Likes Received:
    1,005
    Location:
    USA
    First Language:
    English
    Primarily Uses:
    RMMV
    The dev console is your friend! It's the most powerful thing you have as a MV plugin developer without an integrated debugger.
    Press F8 during a playtest to open it, and you'll see that your code has a syntax error, which is why none of it is loading.

    2018-03-24 08_07_44-Developer Tools - chrome-extension.png

    Some additional things:
    1. You should alias the Scene_Item.prototype.create method; right now you're overwriting it. Instead of overwriting it, with an alias you call the old version and then only add the code that you need.
      • This reduces compatibility issues between different plugins that work on the same functions
      • Reduces the chance of making an error. For example, you forgot to include the creation of the actor window in your code, but wouldn't be an issue with an alias
    Code:
    //example of an alias
    var jimminy_alias_Scene_Item_create = Scene_Item.prototype.create;
    //The "old" function is now stored in a variable
    Scene_Item.prototype.create = function() {
        //we call the "old" function
        jimminy_alias_Scene_Item_create.call(this);
        //now we add just the new stuff that we need
        this._itemPicturesWindow = new Window_DrawTheItemPictures();
        this.addWindow(this._itemPicturesWindow);
        this._itemWindow._itemPicturesWindow = this._itemPicturesWindow;
    };
    
    2. Right now the image has to be the same name as the item (i.e. "Potion"). If you wanted to use the same image for multiple items, you can change it like this:

    Code:
    Window_DrawTheItemPictures.prototype.setImageName = function(item) {
    this._imageName = item.meta.image;
    this.refresh();
    };
    
    And in your database in the notetag of the item, you would write:
    <image:MyImageName>
    (Changing MyImageName of course).

    3. Preloading / reserving images:
    Here is an example of the base RPG Maker MV code reserving all the party's faces in the menu so that they are displayed right away (instead of a delay while it waits for the pictures to load)

    Code:
    Window_Base.prototype.reserveFaceImages = function() {
        $gameParty.members().forEach(function(actor) {
            ImageManager.reserveFace(actor.faceName());
            }, this);
    };
    
    Scene_Menu.prototype.createStatusWindow = function() {
        this._statusWindow = new Window_MenuStatus(this._commandWindow.width, 0);
        this._statusWindow.reserveFaceImages();
        this.addWindow(this._statusWindow);
    };
    
     
    #4
  5. Jimminybob

    Jimminybob Villager Member

    Messages:
    24
    Likes Received:
    7
    First Language:
    English
    Primarily Uses:
    RMMV
    Apologies it's taken me so long to respond to this.

    Thanks very much on the aliasing and also the notetag additions, I'll be sure to change the code to take those into account, and the notetags will come in very handy. Also I'll be sure to include the preloading code too, I'm assuming that in my case I would simply loop through the items in my database and reserve the notetag values for image names.

    I managed to fix the issue I was having with my code, was a rogue bracket that I missed my text editor warning me about. However it seems like I am now getting another error when it loads into the game itself (past the menu) of:

    "TypeError: Cannot read property 'setImageName' of undefined"

    I'm guessing it relates to the function "refreshMyItemPictureWindow"

    Code:
    Window_ItemList.prototype.refreshMyItemPictureWindow = function(item) {
            this._itemPicturesWindow.setImageName(item);
        };
    and since it's saying that this._itemPictures Window is undefined, could this be an issue with the select function not passing in the item correctly, or do I need to do some extra declaration within that function itself? My code hasn't changed a great deal since what I posted above if you need to see the whole section.

    Thanks
     
    #5
  6. Aloe Guvner

    Aloe Guvner Walrus Veteran

    Messages:
    1,600
    Likes Received:
    1,005
    Location:
    USA
    First Language:
    English
    Primarily Uses:
    RMMV
    If you open the developer console (press F8 on the keyboard), you won't have to guess, it will tell you exactly where the error is :)

    But yes, it means that "this._itemPicturesWindow" (where "this" is the instance of Window_ItemList) is undefined at that exact moment.
    It could be that the Window_ItemList 'select' method is called as soon as it gets created, that happens for some of the windows.

    Try modifying these two parts.

    Code:
    Window_ItemList.prototype.refreshMyItemPictureWindow = function(item) {
       //only update the _itemPicturesWindow if it is defined
       if (this._itemPicturesWindow !== undefined) {
           this._itemPicturesWindow.setImageName(item);
       }
    };
    
    Code:
       Scene_Item.prototype.create = function() {
           Scene_ItemBase.prototype.create.call(this);
           this.createCategoryWindow();
           this.createHelpWindow();
           this.createItemWindow();
    
           this._itemPicturesWindow = new Window_DrawTheItemPictures();
           this.addWindow(this._itemPicturesWindow);
           this._itemWindow._itemPicturesWindow = this._itemPicturesWindow;
           
           console.log("I just added _itemPicturesWindow as a property of _itemWindow !!");
           //Press F8 to open the console and make sure this message gets logged 
       };
    
     
    #6
  7. Jimminybob

    Jimminybob Villager Member

    Messages:
    24
    Likes Received:
    7
    First Language:
    English
    Primarily Uses:
    RMMV
    Hi there,

    Yeah as soon as I posted I remembered the console! Even though you'd just mentioned it, it completely slipped my mind.

    But yes the problem was that it was trying to pass in an empy item object, so i've added in a similar check to what you posted to check to see if its populated first, and that has fixed the issue.

    So it all is working quite nicely now. Just need to sort out the preloading of images and have also got a small problem where moving between items overlays the images rather than replacing them, so i'm just going to add in a bit to clear the image first and hopefully that should be it done.

    Thanks very much for your help on this, it's been invaluable!
     
    #7
    Aloe Guvner likes this.

Share This Page