Adding displayverb to shop items you just sold

Forgewright
03 Apr 2020, 08:55Edited post
When using: "Setting up a shop". The uncloned items in inventory will not show "Sell" in the inventory item's drop-down box of verbs.
I am attempting to do this through the player.changedparent script. You can see the addition I made.
I get an error stating: Can not modify the contents of the list as it is defined by an inherited type. And it needs to be cloned first.
What say ye?
Edited script
if (HasAttribute(game.pov.parent, "is_shop")) {
foreach (object, GetDirectChildren (player)) {
list add (object.displayverbs, "Sell")
}
}
if (game.pov = this) {
if (IsDefined("oldvalue")) {
OnEnterRoom (oldvalue)
}
else {
OnEnterRoom (null)
}
if (game.gridmap) {
MergePOVCoordinates
}
}
this.hasbeenmoved = true

Dcoder
03 Apr 2020, 09:58First, I haven't used Pixie's Shop library, although I've created my own shop in my game(s).
Second, you should not use the player.changedparent script -- it is not necessary to do it there, and more unnecessary code there will add overhead to your game.
It sounds like you want to add the "Buy" displayverb to items the player sells to the shop, in case he wants to buy them back again later? If so, can't you just put the list add (object.displayverbs, "Buy")
code into your "sell" command? Maybe I'm not understanding your intent.

Dcoder
03 Apr 2020, 10:09Oh, you're trying to add the "Buy" displayverb to all the new items the shop is selling. In that case, why are the items in the player's inventory instead of in the shop room, i.e. foreach (object, GetDirectChildren (player)
? Maybe that should be foreach (object, GetDirectChildren (game.pov.parent)
? (You'll have to remove the player from the object list.)

Dcoder
03 Apr 2020, 10:16KV had a simple way of copying an object's displayverbs list so that it could be modified:
object.displayverbs = ListExclude(object.displayverbs, "")
http://textadventures.co.uk/forum/samples/topic/d1-rxlik1ewi-zhkb5mqoa/easy-way-to-alter-an-objects-displayverbs-list

Dcoder
03 Apr 2020, 10:28So I would start at the source, i.e., the code that adds/creates the sellable items to the shop (instead of player.changedparent) -- add scripting there that iterates through the list of sellable items with a foreach
script, and run 1) KV's code on each item, plus 2) the list add (object.displayverbs, "Buy")
code on each item.
I will try to follow up with a coded example.
@mrangel -
Feel free to jump in if you feel the need...

Forgewright
03 Apr 2020, 10:47So sorry, Dcoder. Somehow I put in the wrong verb and explanation. Was late here. Was last working on buy script and goofed up on post.
Buy should be sell.
When player is in a shop, all of their inventory should have a sell verb added.
Right now they can type it in but I would like to see the sell button.
When they move around there will be no sell button till they enter a shop flagged shop. Then when they leave it disappears.

Dcoder
03 Apr 2020, 10:47A simple example -- I made a few sellable objects and put them in the room YourShop
:
SellablesList = GetDirectChildren(YourShop)
list remove (SellablesList, game.pov)
foreach (item, SellablesList) {
item.displayverbs = ListExclude(item.displayverbs, "")
list add (item.displayverbs, "Buy")
}
Now each (sellable) item in YourShop
minus the player will have the "Buy" displayverb added to it.

Dcoder
03 Apr 2020, 10:58Ok, in that case, go to the Shop room's "Scripts" tab, where it says "After entering the room:", and put in this code:
PlayerSellList = GetDirectChildren(game.pov)
foreach (item, PlayerSellList) {
item.inventoryverbs = ListExclude(item.inventoryverbs, "")
list add (item.inventoryverbs, "Sell")
}
To get rid of all the "Sell" inventoryverbs when the player leaves the Shop is a bit trickier, but definitely doable. More code to follow...

Forgewright
03 Apr 2020, 11:05That worked like a charm

Dcoder
03 Apr 2020, 11:09Actually, getting rid of all the "Sell" verbs should be easier. Just put this in the Shop's "After leaving the room:" section:
PlayerSellList = GetAllChildObjects(game.pov)
foreach (item, PlayerSellList) {
list remove (item.inventoryverbs, "Sell")
}
I think that that will be OK. Trying to remove the "Sell" inventoryverb from an object that doesn't have it in its list should be OK.
I used GetAllChildObjects
instead of GetDirectChildren
in case the player put sellable objects in containers he was carrying, while he was in the shop.

Forgewright
03 Apr 2020, 11:24Works on entry. but if I buy it back it has two sell verbs. and when I sell it it has two buy verbs... This is probably something else on my end. Will have to investigate...

Dcoder
03 Apr 2020, 11:44Modified code for the Shop's "After entering the room:" section:
PlayerSellList = GetDirectChildren(game.pov)
foreach (item, PlayerSellList) {
item.inventoryverbs = ListExclude(item.inventoryverbs, "")
if (not ListContains(item.inventoryverbs, "Sell")) {
list add (item.inventoryverbs, "Sell")
}
}

Dcoder
03 Apr 2020, 11:58Conversely, modified Buy code for the Shop, also add to Shop's "After entering..." section:
ShopSellList = GetDirectChildren(YourShop)
list remove (ShopSellList, game.pov)
foreach (item, ShopSellList) {
item.displayverbs = ListExclude(item.displayverbs, "")
if (not ListContains(item.displayverbs, "Buy")) {
list add (item.displayverbs, "Buy")
}
}
One issue is that items you have just sold will still have their "Sell" verb and no "Buy" verb, and items you have just bought will still have their "Buy" verb and no "Sell" verb, until you re-enter the Shop.
To resolve this, either a) put list remove
and list add
scripts on your "Sell" and "Buy" commands, so that the item's display/inventory verbs will be updated immediately, or b) have a local turn script (in the Shop) run the code every turn.

Dcoder
03 Apr 2020, 12:26I'll make it simple -- delete all the above code and start over. Put this in the Shop's "Scripts" tab as a local turn script:
PlayerSellList = GetAllChildObjects(game.pov)
foreach (item, PlayerSellList) {
item.inventoryverbs = ListExclude(item.inventoryverbs, "")
if (not ListContains(item.inventoryverbs, "Sell")) {
list add (item.inventoryverbs, "Sell")
list remove (item.inventoryverbs, "Buy")
}
}
ShopSellList = GetDirectChildren(YourShop)
list remove (ShopSellList, game.pov)
foreach (item, ShopSellList) {
item.displayverbs = ListExclude(item.displayverbs, "")
if (not ListContains(item.displayverbs, "Buy")) {
list add (item.displayverbs, "Buy")
list remove (item.displayverbs, "Sell")
}
}
This will put a "Sell" verb on everything the player is carrying and remove any "Buy" verbs. The reverse is true of the Shop's merchandise. It's overkill but it should work.
Then, in the Shop's "After leaving the room:" section:
foreach (item, GetAllChildObjects(game.pov)) {
list remove (item.inventoryverbs, "Sell")
}

Forgewright
03 Apr 2020, 14:12As far as I can tell after some testing, you have got this running 100%. Impressive
mrangel
03 Apr 2020, 15:18(edited slightly because I don't know the shop system that well; fixed a minor issue)
That seems a bigger piece of code than I would have expected.
I'd comment that item.inventoryverbs = ListExclude(item.inventoryverbs, "")
is inefficient because you're looping over the whole list to check if any of the elements is ""
. I think you can just do item.inventoryverbs = item.inventoryverbs
; because assignment to an object attribute implicitly serialises lists and dictionaries. ISTR the ListExclude
trick is only necessary if the thing to the left hand side of the =
is a local variable.
Also a little confused by list remove (item.inventoryverbs, "Buy")
- surely "Buy" will never be on an object's inventoryverbs, and "Sell" will never be in its displayverbs. Or do the existing parts of your shop script add the verbs to both lists?
I'd probably have done it by adding a few lines to a core function:
<function name="GetDisplayVerbs" parameters="object" type="stringlist">
if (Contains(game.pov, object)) {
baselist = object.inventoryverbs
shopverb = "Sell"
}
else {
baselist = object.displayverbs
shopverb = "Buy"
}
if (not game.autodisplayverbs or GetBoolean(object, "usestandardverblist") or not HasAttribute(game, "verbattributes")) {
return (baselist)
}
else {
if (HasAttribute(object, "generatedverbslist")) {
verbs = object.generatedverbslist
}
else {
verbs = NewStringList()
foreach (attr, GetAttributeNames(object, false)) {
if (ListContains(game.verbattributes, attr)) {
cmd = ObjectDictionaryItem(game.verbattributeslookup, attr)
if (HasString(cmd, "displayverb")) {
displayverb = CapFirst(cmd.displayverb)
}
else {
displayverb = CapFirst(attr)
}
if (not ListContains(baselist, displayverb)) {
list add (verbs, displayverb)
}
}
}
object.generatedverbslist = verbs
} if (GetBoolean (object, "sell") and HasAttribute(game.pov.parent, "is_shop")) {
verbs = ListCombine (Split (shopverb), ListExclude (verbs, Split("Buy;Sell")))
}
if (GetBoolean(object, "useindividualverblist")) {
return (verbs)
}
else {
return (ListCombine(baselist, verbs))
}
}
</function>

Forgewright
03 Apr 2020, 15:54Have to make sure any items that start in the shop have their display verbs set correctly. Those items were giving me grief but setting the verbs fixed it. We'll see what happens down the road.
Kinda scared to touch it now Mrangel. But I will make another copy of the game to test your code.

Dcoder
05 Apr 2020, 12:07@Forgewright -
Ok, as per mrangel, in my local turnscript, just replace the 2 lines in bold (they are replaced below). This will reduce some of the overhead:
PlayerSellList = GetAllChildObjects(game.pov)
foreach (item, PlayerSellList) {
item.inventoryverbs = item.inventoryverbs
if (not ListContains(item.inventoryverbs, "Sell")) {
list add (item.inventoryverbs, "Sell")
list remove (item.inventoryverbs, "Buy")
}
}
ShopSellList = GetDirectChildren(YourShop)
list remove (ShopSellList, game.pov)
foreach (item, ShopSellList) {
item.displayverbs = item.displayverbs
if (not ListContains(item.displayverbs, "Buy")) {
list add (item.displayverbs, "Buy")
list remove (item.displayverbs, "Sell")
}
}
@mrangel -
As for the buy/sell verbs, when you buy an item from the store, the buy verb will still be on the bought item in the player's inventory; the sell verb will still be on an item after being sold to the shop. In order to update those verbs immediately, that is why the additional listremove
lines are there.
mrangel
05 Apr 2020, 13:27I think you misunderstood my question; but in explaining why I get "sorry, you can't post that here".
So a shortened form: Is there ever a reason why the player would need buy an object that's in their inventory; or sell an object they don't own?
It seems to me that "Buy" would only go in the object's displayverbs
, and "Sell" should only go in inventoryverbs
.
So I questioned why your code is removing those verbs from lists I think they would never be in.

Dcoder
05 Apr 2020, 14:09How about this:
PlayerSellList = GetAllChildObjects(game.pov)
foreach (item, PlayerSellList) {
item.inventoryverbs = item.inventoryverbs
if (not ListContains(item.inventoryverbs, "Sell")) {
list add (item.inventoryverbs, "Sell")
list remove (item.displayverbs, "Buy") // This line changed
}
}
ShopSellList = GetDirectChildren(YourShop)
list remove (ShopSellList, game.pov)
foreach (item, ShopSellList) {
item.displayverbs = item.displayverbs
if (not ListContains(item.displayverbs, "Buy")) {
list add (item.displayverbs, "Buy")
list remove (item.inventoryverbs, "Sell") // This line changed
}
}
mrangel
05 Apr 2020, 16:19That's what I would have expected it to look like :)

Forgewright
05 Apr 2020, 16:56I just realized that I have not paid attention to the 'Display and Inventory' differences. This would be a cause of some of my grief. Such an obvious part but I missed it. I thought the game would control this from the settings under the object verb set up. Completely went over my head. Dang

Forgewright
05 Apr 2020, 17:25Ok, this is where I stand now.
sell #object#
command:
if (not HasAttribute(game.pov.parent, "stock")) {
ClearTurn
msg ("You can't sell stuff here.")
}
else if (not object.parent = game.pov) {
ClearTurn
msg ("You're not carrying " + object.article + ".")
}
else if (HasAttribute(object, "burned")) {
if (object.burned = true) {
ClearTurn
msg (player.parent.owner.alias + " says,<br><i>I have no use for a used torch.")
}
}
else if (HasAttribute(object, "sell")) {
ClearTurn
game.selling_object = object
Ask ("I will give you " + game.selling_object.price + " gold. OK?") {
object = game.selling_object
if (result) {
msg ("You sell " + object.article + " for " + object.price + " gold.")
game.pov.money = game.pov.money + object.price
if (GetBoolean(object, "cloneme")) {
RemoveObject (object)
}
else {
object.parent = game.pov.parent.stock
SetUpMerchandise (object)
}
}
else {
msg ("You turn down the offer of " + object.price + " gold.")
}
}
}
Then the buy #object#
command.
if (object.price > game.pov.money) {
msg ("You can't afford that!")
}
else {
object.take = true
object.parent = game.pov
object.buy = null
object.listalias = object.alias
list remove (object.inventoryverbs, "Buy")
player.money = game.pov.money - BuyingPrice(object)
msg ("You decide to buy " + object.article + " for " + DisplayMoney(BuyingPrice(object)) + ".")
}
Next comes the SetUpMerchandise
Function used above with obj
parameter.
if (not HasString(obj, "alias")) {
obj.alias = obj.name
}
obj.listalias = obj.alias + " (" + DisplayMoney(BuyingPrice(obj)) + ")"
obj.cloneme = false
obj.take => {
StealObject (this)
}
obj.buy => {
BuyObject (this)
}
Next is the BuyObject
function also with the obj
parameter used in SetUpMechandise
function.
if (obj.price > game.pov.money) {
msg ("You can't afford that!")
}
else {
if (GetBoolean(obj, "cloneme")) {
obj = CloneObject(obj)
}
list remove (obj.inventoryverbs, "Buy")
obj.take = true
obj.parent = game.pov
obj.buy = null
obj.listalias = obj.alias
player.money = game.pov.money - BuyingPrice(obj)
msg ("You buy " + obj.article + " for " + DisplayMoney(BuyingPrice(obj)) + ".")
}
So far everything checks out and I have yet to find a bug. That really isn't saying a lot though.

Forgewright
05 Apr 2020, 17:36Oh. And the GetDisplayVerbs function is awesome.
I don't have items with multi states like a torch having an inventory verb 'Sell' because the use of a torch will not be allowed to sell. And I have not decided how and where to put the script for changing the inventory verb if it is not burned. However, the player can type it in and sell it if the torch is not used. If it is the shop owner tells them he has no use for a burned torch.
I thought that was a cool thing to do. Eh...
mrangel
05 Apr 2020, 19:03Another alternate way to do this :¬p
In the player's changedparent
script, add a line
if (game.pov = this) {
this.is_in_shop = GetBoolean (this.parent, "is_shop")
if (IsDefined("oldvalue")) {
OnEnterRoom (oldvalue)
}
else {
OnEnterRoom (null)
}
if (game.gridmap) {
MergePOVCoordinates
}
}
this.hasbeenmoved = true
And then give him a script changedis_in_shop
:
foreach (obj, ScopeVisibleInventory()) {
obj.inventoryverbs = ListExclude (obj.inventoryverbs, "Sell")
if (this.is_in_shop) {
list add (obj.inventoryverbs, "Sell")
}
}
It would be just as easy to add "Buy" to displayverbs
in the same script; but there's no reason to do so. Because I believe if you followed the "Setting up a shop" guide, the buy and sell scripts will already add and remove the "Buy" verb as necessary.
mrangel
05 Apr 2020, 19:16I don't have items with multi states like a torch having an inventory verb 'Sell' because the use of a torch will not be allowed to sell. And I have not decided how and where to put the script for changing the inventory verb if it is not burned. However, the player can type it in and sell it if the torch is not used. If it is the shop owner tells them he has no use for a burned torch.
I thought that was a cool thing to do. Eh...
I thought of an alternative way to do that.
Where your 'sell' command has:
else if (HasAttribute(object, "burned")) {
if (object.burned = true) {
ClearTurn
msg (player.parent.owner.alias + " says,<br><i>I have no use for a used torch.")
}
}
you could change that to:
else if (HasString (object, "price")) {
ClearTurn
msg (player.parent.owner.alias + " says,<br><i>" + object.price + "</i>")
}
then in the script for using the torch, you could put:
this.burned = true
this.sell = false
this.price = "I have no use for a burned torch."
Changing the price of an object to a string causes the shopkeeper to refuse to accept it; changing sell
to false removes the "Sell" inventory verb (if you're using my GetInventoryVerbs
script above - I just fixed it because I didn't know that there was a sell boolean, so it previously checked if the object has a price)

Forgewright
05 Apr 2020, 21:19Off-topic, but I have been gone a while and was wondering if KV was still active in the forums?

Forgewright
23 Apr 2020, 08:24Wow, I somehow missed these last couple of posts from you Mrangel. I will make those changes.
Thank you Dcoder and Mrangel for your help. You really make my coding time so much less and teach me new and better ways to code.