Making every Food item in the Room or Inventory Rot.

I am having trouble figuring out how to do this...

I have several Food items, each with an Int Attribute 'FRESHNESS'

Each item has its own starting FRESHNESS from 18 to 63, but every turn i want every single item i nthe Player's current room AND inventory to have it's FRESHNESS reduced by 1.

I know I can FRESHNESS = FRESHNESS - 1, but I dont know how work that into making the script find and choose every object with the FRESHNESS attribute, and affect ONLY those objects.


Each item has its own starting FRESHNESS from 18 to 63, but every turn i want every single item i nthe Player's current room AND inventory to have it's FRESHNESS reduced by 1.

So food doesn't go off if the player leaves the room?

Well, that's not a difficult thing to handle. You will want to use a foreach loop, which lets you go over a list of objects and do the same thing to each one.

Something like:

foreach (object, GetAllChildObjects (game.pov.parent)) {
  if (HasInt (object, "FRESHNESS")) {
    object.FRESHNESS = object.FRESHNESS - 1
  }
}

Where object is an arbitrary name; you can call the variable whatever you want; and FRESHNESS is the name of the attribute.

As I previously mentioned, you must not destroy the object within this loop, as foreach generates an error if the list changes before it has finished. But you can do anything else you would normally do with an object.

GetAllChildObjects gets a list of every object that is inside the room, or inside an object inside the room. You could use ScopeReachable() or ScopeVisible() instead; those only deal with objects the player can see (so not objects that are invisible or in a closed box)


So, If I just replace 'game.pov.parent' with 'game', will that affect everything?

If I did that I would want to also only affect objects which have the attribute Prototype, since I do not want the original items that were cloned to rot.


I think i have it working using:

If object has FRESHNESS
  then
  if object has attribute prototype
    then
    fresh = fresh - 1

with:

loop all obj,
if object fresh <= 0
  then
  remove object

now I want a message saying 'your fish has rotted', but is there a way I can have the game print: 'your [FISH ALIAS] has rotted'?


I found AllObjects() and got the Rot to apply to everything, and a way for it to only show a rot message if you are in the room, or when you reach the room.

for Curious parties, so far I have:

foreach (object, AllObjects()) {
  if (HasObject (object, "prototype") and HasInt (object, "FRESHNESS")) {
    object.FRESHNESS = object.FRESHNESS - 1
  }
}
foreach (object, GetAllChildObjects (game.pov.parent)) {
  if (HasAttribute(object, "FRESHNESS")) {
    if (object.FRESHNESS <= 0) {
      if (Contains (game.pov.parent,object)) {
        msg ("A Fish has rotted away.")
      }
      RemoveObject (object)
    }
  }
}

So, If I just replace 'game.pov.parent' with 'game', will that affect everything?

If I did that I would want to also only affect objects which have the attribute Prototype, since I do not want the original items that were cloned to rot.

To affect all items, you would probably want to use AllObjects ().
For all cloned objects, I think it would be:

foreach (object, AllObjects()) {
  if (HasObject (object, "prototype") and HasInt (object, "FRESHNESS")) {
    object.FRESHNESS = object.FRESHNESS - 1 
    if (object.FRESHNESS <= 0) {
      RemoveObject (object)
    }
 ​ }
}

However, this might become slow after time, because it will still keep reducing the freshness of all the removed objects.
One way to avoid this slowdown would be destroying clones rather than removing them; which is easiest to accomplish by making a list of objects to destroy (because foreach and destroy interact oddly)
So it would look like:

foodToRemove = NewStringList()
foreach (object, AllObjects()) {
  if (HasObject (object, "prototype") and HasInt (object, "FRESHNESS")) {
    object.FRESHNESS = object.FRESHNESS - 1 
    if (object.FRESHNESS <= 0) {
      list add (foodToRemove, object.name)
    }
 ​ }
}
foreach (objectname, foodToRemove) {
  destroy (objectname)
}

(destroy is called using a string containing the name of an object, not the object itself… so you make a list of object names)


Looks like we were typing at the same time. That seems valid, but it's probably more efficient to do things inside the same loop. And you will have the problem I mentioned if the number of destroyed objects gets large.


So, i definitely want to destroy with that last example, but when I replace what I last posted with what you last posted, I get:

> fish
You attempt to catch a fish...
You have caught a Roach!
You pick it up.
Error running script: Function not found: '​'

The same Error running scr... prints after every action, wait, or what have you.

Everything still seems to work, and I cannot find where this blank function is.


Maybe I could use my method, but add a:

if object has attribute prototype
  if object.parent = null
    destroy object

?


That's confusing. Can you share a link to the game so I can take a look at it? My first guess would be an extra , somewhere, or a , that should be a ..


Ok, I couldnt find it in Code View, but in the Quest GUI I found an empty call function underneath 'foodtoremove [value] [expression] object.name

I think one too many } at the bottom...


Now I have this and the error is gone: (EDITED TO ADD FISH ROT MESSAGE)

foodToRemove = NewStringList()
foreach (object, AllObjects()) {
  if (HasObject (object, "prototype") and HasInt (object, "FRESHNESS")) {
    object.FRESHNESS = object.FRESHNESS - 1
    if (object.FRESHNESS <= 0) {
      if ((Contains (game.pov.parent,object))) {
        list add (foodToRemove, object.name)
        msg ("A Fish has rotted away...")
      }
    }
  }
}
foreach (objectname, foodToRemove) {
  destroy (objectname)
}

looks identical... I have no idea where the empty call function came from o.O