Take All form Containers

So, I'm trying to set up a universal command "loot" for my game. Ideally, this would handle everything from searching bodies, opening containers, and then taking everything inside. My code correctly identifies dead monsters and containers, and I can even take everything I found on a dead enemy, but I'm getting hung up trying to take everything from a container. So, my command loot #object# has the following code.

if (HasAttribute (object, "monstertype)) {
    if (object.dead = true) {
        HandleSingleCommand ("search " +object.alias)
        HandleSingleCommand ("take all")
    }
    else {
    msg ("You cannot loot a living enemy!")
}
}
else if (HasAttribute (object, "feature_container")) {
    HandleSingleCommand ("open " +object.alias)
    HandleSingleCommand ("take all")
}
else {
msg ("There's nothing here to loot.")
}
}

Everything works fine until I get to the chest. The chest opens, but when I try to "take all," it doesn't take anything. Just tells me the chest can't be lifted (as intended because I've excluded the chest from ALL.) My guess is that the solution has something to do with using a function like GetAllChildObjects (object), but I don't know how to run the function on the #object#, and then how to call up the result to move it to the player's inventory. I've seen an old post about how "take all" should not include children of objects, but I'm hoping there's a way set up some code that would allow the player to type something like "take all chest" in order to get everything out of a chest container. If I could figure that out, I should be able to make my loot command work on anything. I feel like I'm on the right track and getting close, just missing some syntax to make it work.


I can't seem to edit my post... Says "You can't post that here." when I try to update my post. I retyped this from my original code, so I missed the second " after "monstertype. It's there in my code, so no worries.


That's an odd way to go about it. Your loot command takes everything on the body or in the chest, but also everything in the room. Seems an odd way to do it. And if there's multiple monsters in the room with the same name, it'll ask the player which one they want to loot, and then ask them again for the search command. (Although this might not be a problem if your game only has one monster per room).

You'll also find that if the player opens a chest and then tries to loot it, they will get a message about it already being open, which seems like an error.

It might make more sense to use the internal functions rather than running extra commands. Something like:

}
else if (HasAttribute (object, "container")) {
  if (not GetBoolean (object, "isopen")) {
    TryOpenClose (true, object)
  }
  if (GetBoolean (object, "isopen")) {
    foreach (contents, GetDirectChildren (object)) {
      if (not GetBoolean (contents, "not_all")) {
        DoTake (contents, true)
      }
    }
  }
}
else {
  msg ("There's nothing here to loot.")
}

This only tries to open the container if it isn't already open. Then it checks for openness again, and if the container is now open (whether it was open before or not), it takes all the items from it.

It still seems odd that looting a body would also cause you to pick up any items you just dropped; but whether it's possible to fix that would depend on how "search" works. If it's the search command from Pixie's CombatLib, you could do something like:

contents = GetDirectChildren (object)
if (HasString (object, "monstertype")) {
  if (GetBoolean (object, "dead")) {
    do (object, "search")
  }
  else {
    msg ("You cannot loot a living enemy!")
  }
}
else if (GetBoolean (object, "container")) {
  if (not GetBoolean (object, "isopen")) {
    TryOpenClose (true, object)
  }
}
else {
  msg ("That's not something I can loot.")
}
foreach (item, ScopeReachableNotHeld()) {
  if (ListContains (contents, item) and not GetBoolean (object, "not_all")) {
    DoTake (item, true)
  }
}

In this version, I'm searching the object if it's a dead monster, opening it if it's a closed container, then going over the list of reachable objects and looking if any of them were inside the object we were looting. In the case of a chest, those objects will be reachable now because it's open; in the case of the monster, the items will be on the floor so we can take them.

(Also, I would advice checking the attribute container, not feature_container. container is true for containers; while feature_container is used to display the "Container" tab in the editor, so applies to lockable doors too. Also, attributes starting with feature_ are only there for the editor, so are removed from a published game)


Thanks mrangel for all your help. As you have correctly surmised I am integrating Pixie's Combat Library. So I tried plugging in that second bit of code, and the search function works perfectly. That TryOpenClose function is one that I'd never seen before, so that is very helpful. However, nothing seems to get taken object in question and placed into the player's inventory. There must be something amiss with that foreach loop, because reading through it, it seems like it should do exactly what I want it to do, but I'm not having any luck getting it to fire. It doesn't throw any sort of error. It just doesn't do anything. As always though, I really appreciate your help with this.


Ugh… I made a silly mistake. I'm using object for the container you're looting, and item for each item in the contents. So GetBoolean (object, "not_all") in the last if statement should be GetBoolean (item, "not_all").

If that doesn't fix it, then try adding a couple of lines at the end so you can see what it's doing:

msg ("Object contents: <ul><li>"+ FormatList(contents, "</li><li>", "</li><li>", "nothing") + "</li></ul>")
msg ("Available items: <ul><li>"+ FormatList(ScopeReachableNotHeld(), "</li><li>", "</li><li>", "nothing") + "</li></ul>")

That should display a list of objects that were inside whatever you looted, and a list of all objects which can be taken. If an item is on both lists and still isn't being taken, there might be a problem. If there's things that you want to be taken but are missing from one of the lists, we will know which list they're missing from, and that helps us to work out how the script needs to be changed.

That TryOpenClose function is one that I'd never seen before, so that is very helpful.

If you're doing something like this, it's often useful to look at the built in commands. For example, if you do msg (open.script) it will tell you that all the open command does is TryOpenClose (true, object); similarly, the close command's script is as simple as TryOpenClose (false, object).
The take command is a bit more complex because it can handle multiple objects, but all it really does is loop over them and try DoTake on each of them - the second argument tells it whether or not to print each item's alias before telling you whether or not it was taken.


Thank you so much. Great info as always. That modified code is working exactly as intended now.

I've found the list of internal functions included with Quest, and I'm gradually working through understanding them all, but there are quite a few. Also, foreach seems quite powerful, and get boolean and no get boolean are very efficient, so I've started using them with more frequency. Thanks again.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums