Items that can only be sold to certain shops?

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
Is there a way to make it so certain items can only be sold to specific shops? For example, some creatures drop monster parts, but I want the player to only be able to sell those monster parts to the apothecary; the blacksmith won't accept them. Likewise, you can sell weapons to the blacksmith, but not to the apothecary.

I tried searching around for this but could only find threads about unrelated topics.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
39,144
Reaction score
12,416
First Language
English
Primarily Uses
RMMV
I know there are a few plugins that will do this - can't think of any names though, but you might try Yanfly's as a starting point.

An alternative to plugins is to just have your shopkeeper event do a check to see if you have the item of interest in inventory and if you do, offer to buy them from you at whatever cost per piece, and do a Show Choices Yes/No prior to opening the shop menu. It'd look something like this:

Code:
Control Variables: Counter = Monster Parts in Inventory
Conditional Branch: If Counter > 0
  Control Variables: Price = Counter * 5 (if you want 5 gold for each monster part)
  Text: I see you have \v[Counter] Monster Parts there!  Would you be interested in selling them for \v[Price] gold?
  Show Choices: Yes, No
    When Yes
      Change Gold += Price
      Change Items: Monster Parts -= Counter
    When No
      Text: I'll be right here if you change your mind
  End (choices)
End (conditional branch)

Shop Processing
If you have no monster parts, you won't see that conversation at all.
 

ShadowDragon

Veteran
Veteran
Joined
Oct 8, 2018
Messages
1,715
Reaction score
567
First Language
Dutch
Primarily Uses
RMMV
you can try to take a look an burning orca's shop, it has a sell only or use both, but you can if possible use
a switch to exclude items (not tested) but you can read if its possible, if not, you can try the above part.
 

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
I know there are a few plugins that will do this - can't think of any names though, but you might try Yanfly's as a starting point.

An alternative to plugins is to just have your shopkeeper event do a check to see if you have the item of interest in inventory and if you do, offer to buy them from you at whatever cost per piece, and do a Show Choices Yes/No prior to opening the shop menu. It'd look something like this:

Code:
Control Variables: Counter = Monster Parts in Inventory
Conditional Branch: If Counter > 0
  Control Variables: Price = Counter * 5 (if you want 5 gold for each monster part)
  Text: I see you have \v[Counter] Monster Parts there!  Would you be interested in selling them for \v[Price] gold?
  Show Choices: Yes, No
    When Yes
      Change Gold += Price
      Change Items: Monster Parts -= Counter
    When No
      Text: I'll be right here if you change your mind
  End (choices)
End (conditional branch)

Shop Processing
If you have no monster parts, you won't see that conversation at all.
Though this is useful, it wouldn't work in any situation where you didn't want to sell all of your parts at once. You can also give monster parts to be crafted into items, so this wouldn't work if there were only a few parts you wanted to sell and keep the rest.

you can try to take a look an burning orca's shop, it has a sell only or use both, but you can if possible use
a switch to exclude items (not tested) but you can read if its possible, if not, you can try the above part.
I had a look, it seems like there's only an option to limit sellable items to whatever the shop also sells to you. That won't work in this case, as you are not able to buy monster parts anywhere.
 

Shaz

Veteran
Veteran
Joined
Mar 2, 2012
Messages
39,144
Reaction score
12,416
First Language
English
Primarily Uses
RMMV
it wouldn't work in any situation where you didn't want to sell all of your parts at once
Then you just give them a "how many would you like to sell" option, use Input Number, limit between 0 and the number they have, and then do the calculation and exchange.
 

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
I'd also need to do a separate dialog for each separate type of monster part, and it's too many to fit inside a "Show Choices" list. I suppose I can make it work if I like nest a couple "Show Choices" lists inside each other, but at that point it's getting so unwieldy I'd really just prefer a way to limit the item's sell options.
 

Kes

Veteran
Veteran
Joined
Aug 3, 2012
Messages
21,863
Reaction score
11,070
First Language
English
Primarily Uses
RMVXA
[MOVE]JS Plugin Requests[/MOVE]
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
9
Reaction score
3
First Language
English
Primarily Uses
RMMV
Try this - quick and dirty, but should work. Adds a single new plugin command, BUY_ONLY, that affects the next shop command in the same script. See the help for details.

Code:
//=============================================================================
// LWP_RestrictedSelling.js
//=============================================================================
/*:
 * @plugindesc Conversations in battles.
 * @author Logan Pickup
 *
 * @help
 * 
 * ============================================================================
 * Plugin Commands
 * ============================================================================
 * 
 * BUY_ONLY a b c ...
 *      The next shop command will create a shop menu that only buys the listed
 *      items. The items to buy are specified as follows:
 *      wX, e.g. w1: buy only weapon 1.
 *      wtX, e.g. wt1: buy only weapon *type* 1.
 *      iX, e.g. i1: buy only item 1.
 *      itX, e.g. it1: buy only item *type* 1. Normal items=1, key items=2.
 *      aX, e.g. a1: buy only armour 1.
 *      atX, e.g. at1: buy only armour *type* 1.
 *      etX, e.g. et1: buy only equipment type 1 (only applies to armour and weapons).
 * 
 * This plugin command only works once to affect the next shop menu called from the
 * same script. Multiple BUY_ONLY commands do not stack; only the last one takes effect.
 * 
 * Example:
 * 
 * BUY_ONLY i1 it2 w1 w2 wt3 e5
 *      The next shop will only purchase from the player:
 *      - items with ID 1 (Potion in default data)
 *      - key items
 *      - weapons with ids 1 and 2 (Sword and Axe in default data)
 *      - weapon type 3 (flail in the default data - there are no weapons of this type in the default data)
 *      - equipment type 5 (accessory in the default data - the Ring qualifies)
 */
(function() {
    ///////////////////////////////////////////////////////////////////////////////
    // Window_ShopSell
    // This is the window that contains the player's items (it's a Window_ItemList)
    // and already contains a method letting us check if an item can be sold or not
    ///////////////////////////////////////////////////////////////////////////////
    Window_ShopSell.prototype.setRestrictions = function(restrictions) {
        this._restrictions = restrictions;
        console.log("sell window restrictions", this._restrictions);
    };
    const oldWindow_ShopSellIsEnabled = Window_ShopSell.prototype.isEnabled;
    Window_ShopSell.prototype.isEnabled = function(item) {
        if (oldWindow_ShopSellIsEnabled.call(this, item)) {
            if (this._restrictions && this._restrictions.buy) {
                console.log("checking ", item, this._restrictions);
                if (item.wtypeId) {
                    if (this._restrictions.buy.weaponId.indexOf(item.id) !== -1) return true;
                    if (this._restrictions.buy.wType.indexOf(item.wtypeId) !== -1) return true;
                    if (this._restrictions.buy.eType.indexOf(item.etypeId) !== -1) return true;
                }
                if (item.atypeId) {
                    if (this._restrictions.buy.armourId.indexOf(item.id) !== -1) return true;
                    if (this._restrictions.buy.aType.indexOf(item.atypeId) !== -1) return true;
                    if (this._restrictions.buy.eType.indexOf(item.etypeId) !== -1) return true;
                }
                if (item.itypeId) {
                    if (this._restrictions.buy.itemId.indexOf(item.id) !== -1) return true;
                    if (this._restrictions.buy.iType.indexOf(item.itypeId) !== -1) return true;
                }
            } else {
                return true;
            }
        }
        return false;
    };
    
    ///////////////////////////////////////////////////////////////////////////////
    // Scene_Shop
    // Just handles passing the data through to Window_ShopSell
    ///////////////////////////////////////////////////////////////////////////////
    const oldScene_ShopPrepare = Scene_Shop.prototype.prepare;
    Scene_Shop.prototype.prepare = function(goods, purchaseOnly, restrictions) {
        oldScene_ShopPrepare.call(this, goods, purchaseOnly);
        this._restrictions = restrictions;
    };
    const oldScene_ShopCreateSellWindow = Scene_Shop.prototype.createSellWindow;
    Scene_Shop.prototype.createSellWindow = function() {
        oldScene_ShopCreateSellWindow.call(this);
        this._sellWindow.setRestrictions(this._restrictions);
    }
    ///////////////////////////////////////////////////////////////////////////////
    // Game_Interpreter
    // Performs the plugin command, saves the data until the next shop command, and
    // passes the data (via SceneManager) to Scene_Shop
    ///////////////////////////////////////////////////////////////////////////////
    const oldGame_InterpreterSetup = Game_Interpreter.prototype.setup;
    Game_Interpreter.prototype.setup = function(list, eventId) {
        oldGame_InterpreterSetup.call(this, list, eventId);
        this._nextShopRestrictions = {};
    }
    function filterPrefix(prefix) {
        return function(x) {
            return x.startsWith(prefix) && x.length > prefix.length && /[0-9]/.test(x[prefix.length]);
        }
    }
    function getId(x) {
        return Number.parseInt(/[0-9]+/.exec(x)[0]);
    }
    const oldGame_InterpreterPluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        if (/buy_only/i.test(command)) {
            this._nextShopRestrictions.buy = {
                itemId: args.filter(filterPrefix('i')).map(getId),
                armourId: args.filter(filterPrefix('a')).map(getId),
                weaponId: args.filter(filterPrefix('w')).map(getId),
                iType: args.filter(filterPrefix('it')).map(getId),
                aType: args.filter(filterPrefix('at')).map(getId),
                wType: args.filter(filterPrefix('wt')).map(getId),
                eType: args.filter(filterPrefix('et')).map(getId),
            };
            return;
        }
        oldGame_InterpreterPluginCommand.call(this, command, args);
    };
    
    const oldGame_InterpreterCommand302 = Game_Interpreter.prototype.command302;
    Game_Interpreter.prototype.command302 = function() {
        if (this._nextShopRestrictions.buy) {
            // straight replacement, no fallback
            if (!$gameParty.inBattle()) {
                var goods = [this._params];
                while (this.nextEventCode() === 605) {
                    this._index++;
                    goods.push(this.currentCommand().parameters);
                }
                SceneManager.push(Scene_Shop);
                SceneManager.prepareNextScene(goods, this._params[4], this._nextShopRestrictions);
                this._nextShopRestrictions = {};
            }
            return true;
        } else {
            // fallback so if something changes original shop creation, it will only
            // break restricted shops, not all shops
            return oldGame_InterpreterCommand302.call(this);
        }
    };
    
})();
 

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
(Sorry for the late reply, been very busy)

This works great!
Is there any way to hide the non-sellable items completely from the sell menu? It's okay if not, I'm just worried about the list getting bogged down if the player collects a whole bunch of items that can't be sold to a particular shop.
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
9
Reaction score
3
First Language
English
Primarily Uses
RMMV
Yep, it turns out that's pretty simple. New version below:

Code:
//=============================================================================
// LWP_RestrictedSelling.js
//=============================================================================

/*:
 * @plugindesc Conversations in battles.
 * @author Logan Pickup
 *
 * @help
 * 
 * ============================================================================
 * Plugin Commands
 * ============================================================================
 * 
 * BUY_ONLY a b c ...
 *      The next shop command will create a shop menu that only buys the listed
 *      items. The items to buy are specified as follows:
 *      wX, e.g. w1: buy only weapon 1.
 *      wtX, e.g. wt1: buy only weapon *type* 1.
 *      iX, e.g. i1: buy only item 1.
 *      itX, e.g. it1: buy only item *type* 1. Normal items=1, key items=2.
 *      aX, e.g. a1: buy only armour 1.
 *      atX, e.g. at1: buy only armour *type* 1.
 *      etX, e.g. et1: buy only equipment type 1 (only applies to armour and weapons).
 * 
 * This plugin command only works once to affect the next shop menu called from the
 * same script. Multiple BUY_ONLY commands do not stack; only the last one takes effect.
 * 
 * Example:
 * 
 * BUY_ONLY i1 it2 w1 w2 wt3 e5
 *      The next shop will only purchase from the player:
 *      - items with ID 1 (Potion in default data)
 *      - key items
 *      - weapons with ids 1 and 2 (Sword and Axe in default data)
 *      - weapon type 3 (flail in the default data - there are no weapons of this type in the default data)
 *      - equipment type 5 (accessory in the default data - the Ring qualifies)
 */

(function() {

    ///////////////////////////////////////////////////////////////////////////////
    // Window_ShopSell
    // This is the window that contains the player's items (it's a Window_ItemList)
    // and already contains a method letting us check if an item can be sold or not
    ///////////////////////////////////////////////////////////////////////////////

    Window_ShopSell.prototype.setRestrictions = function(restrictions) {
        this._restrictions = restrictions;
        console.log("sell window restrictions", this._restrictions);
    };

    const oldWindow_ShopSellIncludes = Window_ShopSell.prototype.includes;
    Window_ShopSell.prototype.includes = function(item) {
        return oldWindow_ShopSellIncludes.call(this, item) && this.isAllowed(item);
    };

    Window_ShopSell.prototype.isAllowed = function(item) {
        if (this._restrictions && this._restrictions.buy) {
            console.log("checking ", item, this._restrictions);
            if (item.wtypeId) {
                if (this._restrictions.buy.weaponId.indexOf(item.id) !== -1) return true;
                if (this._restrictions.buy.wType.indexOf(item.wtypeId) !== -1) return true;
                if (this._restrictions.buy.eType.indexOf(item.etypeId) !== -1) return true;
            }
            if (item.atypeId) {
                if (this._restrictions.buy.armourId.indexOf(item.id) !== -1) return true;
                if (this._restrictions.buy.aType.indexOf(item.atypeId) !== -1) return true;
                if (this._restrictions.buy.eType.indexOf(item.etypeId) !== -1) return true;
            }
            if (item.itypeId) {
                if (this._restrictions.buy.itemId.indexOf(item.id) !== -1) return true;
                if (this._restrictions.buy.iType.indexOf(item.itypeId) !== -1) return true;
            }
            return false;
        } else {
            return true;
        }
    }
    
    const oldWindow_ShopSellIsEnabled = Window_ShopSell.prototype.isEnabled;
    Window_ShopSell.prototype.isEnabled = function(item) {
        return oldWindow_ShopSellIsEnabled.call(this, item) && this.isAllowed(item);
    };
    
    ///////////////////////////////////////////////////////////////////////////////
    // Scene_Shop
    // Just handles passing the data through to Window_ShopSell
    ///////////////////////////////////////////////////////////////////////////////

    const oldScene_ShopPrepare = Scene_Shop.prototype.prepare;
    Scene_Shop.prototype.prepare = function(goods, purchaseOnly, restrictions) {
        oldScene_ShopPrepare.call(this, goods, purchaseOnly);
        this._restrictions = restrictions;
    };

    const oldScene_ShopCreateSellWindow = Scene_Shop.prototype.createSellWindow;
    Scene_Shop.prototype.createSellWindow = function() {
        oldScene_ShopCreateSellWindow.call(this);
        this._sellWindow.setRestrictions(this._restrictions);
    }

    ///////////////////////////////////////////////////////////////////////////////
    // Game_Interpreter
    // Performs the plugin command, saves the data until the next shop command, and
    // passes the data (via SceneManager) to Scene_Shop
    ///////////////////////////////////////////////////////////////////////////////

    const oldGame_InterpreterSetup = Game_Interpreter.prototype.setup;
    Game_Interpreter.prototype.setup = function(list, eventId) {
        oldGame_InterpreterSetup.call(this, list, eventId);
        this._nextShopRestrictions = {};
    }

    function filterPrefix(prefix) {
        return function(x) {
            return x.startsWith(prefix) && x.length > prefix.length && /[0-9]/.test(x[prefix.length]);
        }
    }

    function getId(x) {
        return Number.parseInt(/[0-9]+/.exec(x)[0]);
    }

    const oldGame_InterpreterPluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        if (/buy_only/i.test(command)) {
            this._nextShopRestrictions.buy = {
                itemId: args.filter(filterPrefix('i')).map(getId),
                armourId: args.filter(filterPrefix('a')).map(getId),
                weaponId: args.filter(filterPrefix('w')).map(getId),
                iType: args.filter(filterPrefix('it')).map(getId),
                aType: args.filter(filterPrefix('at')).map(getId),
                wType: args.filter(filterPrefix('wt')).map(getId),
                eType: args.filter(filterPrefix('et')).map(getId),
            };
            return;
        }
        oldGame_InterpreterPluginCommand.call(this, command, args);
    };
    
    const oldGame_InterpreterCommand302 = Game_Interpreter.prototype.command302;
    Game_Interpreter.prototype.command302 = function() {
        if (this._nextShopRestrictions.buy) {
            // straight replacement, no fallback
            if (!$gameParty.inBattle()) {
                var goods = [this._params];
                while (this.nextEventCode() === 605) {
                    this._index++;
                    goods.push(this.currentCommand().parameters);
                }
                SceneManager.push(Scene_Shop);
                SceneManager.prepareNextScene(goods, this._params[4], this._nextShopRestrictions);
                this._nextShopRestrictions = {};
            }
            return true;
        } else {
            // fallback so if something changes original shop creation, it will only
            // break restricted shops, not all shops
            return oldGame_InterpreterCommand302.call(this);
        }
    };
    
})();
 
Last edited:

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
Works great in the shop! However, I am getting this error when I try to open my item list on the main menu:

itemrestrict.png
 

theartofme

Villager
Member
Joined
Feb 21, 2019
Messages
9
Reaction score
3
First Language
English
Primarily Uses
RMMV
Silly mistake, my bad! The line:
Code:
    Window_ItemList.prototype.includes = function(item) {
Should be:
Code:
    Window_ShopSell.prototype.includes = function(item) {
I've updated the post above with the fix, too.
 

Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
86
Reaction score
20
First Language
English
Primarily Uses
RMMV
Sorry again for the SUPER late response, but this works perfectly! Thank you!
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Here's the graveyard area I'm currently working on for another small project! (This is actually an RPG and not a puzzle game for once)
In the strangest turn of events, my animations now live in the characters folder, and my character images now live in the pictures folder. It's a complicated world, but weirdly a more resource efficient one. :LZSexcite:
Did you know that we use robotic spy animals to learn about how different species behave in the wild? We're only a step away from robotic spy humans, and then Terminator. :p
Not being able to do a commission/request after all and having to give back the money is so heartbreaking.

Sometimes I'm just way too eager to help people out and suddenly find myself in a very deep hole because I'm not gonna be able to finish it. I really hope I don't come across as unprofessional to these people and in general. =n="

Forum statistics

Threads
98,049
Messages
948,870
Members
129,319
Latest member
AikoKurasu
Top