Error concerning exits

Gng

Heyall

I've encountered a rather peculiar error.
My code uses two while functions. The first one is for finding exits and setting them to visible again and the second one is for picking 3 random exits and setting exit.visible to false.
Now when I use the while separately they work, but when I add them together it returns errors. The errors are either object not set to an instance of an object or can't set the object to element.

First While:

while (ListCount(z) > 0) {
  this.exi = PickOneObject (z)
  list remove (z, this.exi)
  this.exi.visible = true
}
It works!

Second While:

while (run forest.repli < 3) {
  this.nexi = PickOneObject (c)
  list remove (c, this.nexi)
  run forest.repli = run forest.repli + 1
  this.nexi.visible = false
}
It works!

The whole code:

run forest.repli = 0
run forest.matlist = run forest.matlistbase
// run forest.exitlistbase is an objectlist of the room exits created in start script. I did it this way because //ScopeExitsForRoom(room) won't show exits that are not visible.
z = run forest.exitlistbase
c = run forest.exitlistbase
d = run forest.exitlistbase
while (ListCount(z) > 0) {
  this.exi = PickOneObject (z)
  list remove (z, this.exi)
  this.exi.visible = true
}
while (run forest.repli < 3) {
  this.nexi = PickOneObject (c)
  list remove (c, this.nexi)
  run forest.repli = run forest.repli + 1
  this.nexi.visible = false
}

Error running script: Error compiling expression 'this.nexi': RootExpressionElement: Cannot convert type 'Object' to expression result of 'Element'

Also using ScopeExitsForRoom(room) won't consider invisible exits. So I had to make an objectlist of the exits in the start script of the game object and then make exits invisible by using code instead of UI.
Is there a way to scope exits that also include invisible exits for a particular room?


The problem is that lists are passed by reference. So your variables z, c, and d are references to the same list in memory.

Your first loop removes all the elements from the list z, which also removes the elements from run forest.exitlistbase, and from its other list references c and d.

Then the second loop runs, and you try to do this.nexi = PickOneObject (c), which sets this.nexi to null because list c doesn't contain any objects. And you get a weird and confusing error as a result.

Why does the first loop need to remove the elements from the list? Wouldn't it be simpler to make it:

foreach (exi, z) {
  exi.visible = true
}

which does the same thing, except it doesn't remove all the elements from the list (and also doesn't cause a load of unnecessary processor load generating random numbers so it can make the objects visible in a random order)


Gng

I see. Well that's weird. Why would it remove from run forest.exitlistbase? I thought it'd only remove from the z reference.

What if instead of z, c and d, I make new run forest attributes? This way it would store the object list.

Your code is much simpler, I hadn't noticed.


I see. Well that's weird. Why would it remove from run forest.exitlistbase? I thought it'd only remove from the z reference.

Lists and dictionaries are passed by reference; in this case, z is simply a reference to run forest.exitlistbase, not a copy of the list. It's one of the quirks you have to get used to if you use them a lot.
This is how functions can change a list which is passed to them - which is sometimes a very useful feature.

What if instead of z, c and d, I make new run forest attributes? This way it would store the object list.

That's right. If the part to the left of the = is an object attribute, it will create a new list, copying the contents.

That's similar to why in some of the core code, you have lines like object.inventoryverbs = object.inventoryverbs - forcing the engine to create a new list, separate from other references to it.

Your code is much simpler, I hadn't noticed.

The foreach method is the quickest way to do something to every member of a list. Where possible, you should use that rather than removing elements from the list.

Looking at your full code, it looks like you are making all the exits visible, then making three of them not visible again. In this case, it might also make more sense to do it in the opposite order - make three of them notvisible, and then make the rest visible, so that the notvisible ones don't need to be changed twice.

I also note from your comment that you tried ScopeExitsForRoom but found that it doesn't find non-visible exits. In this case, you could use FilterByAttribute (AllExits (), "parent", Name of room) to get all exits in the room.

So your code would be something like:

exitlist = FilterByAttribute (AllExits (), "parent", run forest)
for (i, 1, 3) {
  exi = PickOneObject (exitlist)
  exi.visible = false
}
foreach (exi, exitlist) {
  exi.visible = true
}

(assuming that run forest is the room)
Get a list of exits in the room; pick three of them at random and set their visible to false; and then loop over the ones that are still in the list making visible true.


Gng

"My God, I get it now..." Thanks for the reference tip.

Your Scope using filterbyattribute is very nice too. I had first tried using it, but instead with FilterByType and use type "exit", but I guess this function doesn't work for exits so I abandoned the idea. The filtering by "parent" fixes my problem.

The reason I make the exits visible first is because this function is a loop; the player uses the exit which leads back to the same room over and over and the function fires again. Since in the last run, 3 of the exits are invisible, I need them to be visible again first and then remove three of them.
I used While in combination with PickOneObject because I want the three invisible exits to be random each time. I think the FilterByAttribute lists the exits in the same way each time.


Gng

Another question arises.

I want the exits to have look attribute. It works fine but also I want to randomly assign each look with run forest attributes which are "pond" and "river" for example. These attributes are string that describe the scenery.
The this.matlist attribute is a string list containing "pond", "river" and other strings.

this.matlist = this.matlistbase
this.zwar = ScopeExitsForRoom(run forest)
foreach (exi, this.zwar) {
  sti = PickOneString(this.matlist)
  list remove (this.matlist, sti)
  exi.look = this.sti
}

Error running script: Object reference not set to an instance of an object.

I think the problem is that sti is string and this.sti won't accept strings. I don't know how to solve that.
I could make a stringlist of the description itself, but I just want to experiment with it and see if it's possible to di it this way.

SOLVED: I can just use GetAttribute

this.matlist = this.matlistbase
this.zwar = ScopeExitsForRoom(run forest)
foreach (exi, this.zwar) {
  sti = PickOneString(this.matlist)
  list remove (this.matlist, sti)
  pti = GetAttribute (run forest, sti)
  exi.look = pti
}


Log in to post a reply.

Support

Forums