Damascus7

Veteran
Veteran
Joined
Jul 21, 2019
Messages
112
Reaction score
26
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

Global Moderators
Global Mod
Joined
Mar 2, 2012
Messages
43,385
Reaction score
15,195
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

Realist
Veteran
Joined
Oct 8, 2018
Messages
4,421
Reaction score
1,682
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
112
Reaction score
26
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

Global Moderators
Global Mod
Joined
Mar 2, 2012
Messages
43,385
Reaction score
15,195
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
112
Reaction score
26
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
22,512
Reaction score
11,998
First Language
English
Primarily Uses
RMVXA
[MOVE]JS Plugin Requests[/MOVE]
 

theartofme

Veteran
Veteran
Joined
Feb 21, 2019
Messages
36
Reaction score
47
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
112
Reaction score
26
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

Veteran
Veteran
Joined
Feb 21, 2019
Messages
36
Reaction score
47
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
112
Reaction score
26
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

Veteran
Veteran
Joined
Feb 21, 2019
Messages
36
Reaction score
47
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.
 

Latest Threads

Latest Posts

Latest Profile Posts

Another comic with Backstage Malak...
If you want to ask him anything, you can ask on my DeviantArt page! I'm not sure if you're allowed to do it here.
Forum tip of the day ...
If you get a temporary suspension for being a jerk to people who are trying to help you, and arguing with mods and admins, don't go making new accounts. Chances are, if you p**s us off enough to suspend you for a week, we're not going to hesitate giving you a permanent ban when you give us a reason.
E_6fAErVkAQILXu.png
I'm so close to releasing my game on steam! :blush::blush::blush:
Us:
"This game is awesome! I wish there are more games like this in the future."
Also us:
"I hate how games stopped being original and start copying other successful games."
Should've done this a few days ago but...
PRI_200136808.jpg
Rest in Peace, Sir Clive Sinclair
1940 - 2021
Entrepreneur, Technologist, Father of the Modern British Computing Industry, and protagonist of Legend of ZUN.

Without you, England's games and tech industry wouldn't be where it is.

Forum statistics

Threads
115,291
Messages
1,088,682
Members
149,923
Latest member
R1ck_Str1k3r
Top