Remove a certain clone with random name?

CheeseMyBaby
20 Aug 2018, 08:17Hey,
so I've been looking around for different solutions but haven't found the one that's juuuuust right for me.
Here's what I need to happen:
Someone throws a grenade (the grenade object gets cloned from Void and moved to wherever the player is currently)
After a second it goes boom and then it should disappear.
Everything is working except for the disappearing part.
I've tried lots of things but I just don't know how to make the grenade disappear after detonation.
This is what I have at the moment...
(this is working, I just need to add the part with removing the grenade... the part I have no idea how to)
l1 = ScopeReachable()
l2 = FilterByAttribute (l1, "alias", "grenade")
if (ListCount (l2) < 1) {
}
else {
foreach (obj, l2) {
SetTimeout (1) {
msg ("B O O M !")
msg (ListCount(l2))
}
}
}
EDIT:
If I remove the object "grenade" the 'original' disappear but not the clone and since the game is set during war time there'lll be flying grenades left right and center and it's impossible for me to keep track of the name of the clones.
Hope I'm being clear enough about my problem?
/Cheese

CheeseMyBaby
20 Aug 2018, 08:48Oh, the msg (ListCount(l2))
is there for debugging, so nevermind that.
mrangel
20 Aug 2018, 09:15SetTimeout creates a script attribute which will be executed later; it doesn't have your l2 list.
Does the grenade always go boom after 1 second? Can there be multiple grenades in play at the same time?
I think this is probably what you want:
SetTimeout (1) {
all_grenades = FilterByAttribute (AllObjects(), "alias", "grenade")
// If there will be grenades around that haven't been thrown, in the
// inventory or similar, then you'd need to add another
// FilterByAttribute here to check their "thrown" flag
if (ListCount (all_grenades) = 0) {
error ("Grenade not found")
}
first_grenade = ListItem (all_grenades, 0)
msg ("B O O M!")
destroy (first_grenade.name)
}
If this script is running more than once at the same time (another grenade is thrown before the first one goes boom) then you will have multiple copies of the timeout running at the same time, and each timeout will remove the first (oldest) grenade in play.
If you have grenades in your inventory, then you need a way to distinguish which one has been thrown. I'd suggest setting a flag on them.
If you're using the latest version of the CloneObject function, then you could change the first filter line to:
all_grenades = FilterByAttribute (AllObjects(), "prototype", grenade)
which I think is slightly more efficient. (and will also work if you have grenades with different aliases)
Now, if you had multiple grenades and they can have different timers, it would be a little more complex, because they won't all be going boom in the order they were thrown. The same applies if the grenades sit around in your inventory, and you don't want to destroy them in the same order they were cloned.
Then you'd want to… let me think about this.
mrangel
20 Aug 2018, 09:47Simple solution: Create a list attribute on the game
element. Add a new grenade to the list when it is thrown, and have the timeout remove the first grenade from the list when it detonates it.
More complex solution that allows grenades with different timers and lets them go boom in a sane order. Here's a function that should work. Its parameters are a cloned grenade object, and the number of seconds it's going to wait before going boom.
<function name="ThrowGrenade" parameters="grenade, time"><![CDATA[
timer = GetTimer("grenade_timer")
if (timer = null) {
create timer ("grenade_timer")
timer = GetTimer("grenade_timer")
timer.grenades = NewObjectList()
timer.script => {
this.enabled = false
nexttrigger = 0
remaining = NewObjectList()
foreach (grenade, this.grenades) {
if (grenade.triggertime <= game.timeelapsed) {
grename = grenade.name
if (HasScript (grenade, "detonate")) {
do (grenade, "detonate")
}
else if (ListContains (ScopeReachable(), grenade)) {
msg ("B O O M!")
}
else {
msg ("<small>b o o m</small>")
}
if (not GetObject(grename) = null) {
destroy (grename)
}
}
else {
list add (remaining, grenade)
if (nexttrigger = 0 or grenade.triggertime < nexttrigger) {
nexttrigger = grenade.triggertime
}
}
}
this.grenades = remaining
if (nexttrigger > 0) {
this.trigger = nexttrigger
this.enabled = true
}
}
}
grenade.triggertime = game.timeelapsed + time
list add (timer, "grenades", grenade)
do (timer, "script")
]]></function>
This one will display "B O O M!" if the player is still in the room and "boom" otherwise. If a grenade has a "detonate" script attribute (or verb?!), that will run instead, allowing you to make fancy custom grenades, like smoke bombs or something, and use the same timer for them.
I know this is probably overkill for a simple game, but once I've started thinking about a problem I find it hard to let it drop without solving it.

CheeseMyBaby
20 Aug 2018, 10:05I know this is probably overkill for a simple game, but ...
There's no such thing as overkill MrA!
Thanks a lot!
hegemonkhan
25 Aug 2018, 09:56@ Cheesemybaby:
here's the data structure concept ('stack', like a literal stack of plates or pancakes, and/or queues, a bit more advanced application of the 'stack' concept) in detail:
https://en.wikipedia.org/wiki/Stack_(abstract_data_type)
https://www.tutorialspoint.com/data_structures_algorithms/stack_algorithm.htm
https://www.cs.cmu.edu/~adamchik/15-121/lectures/Stacks%20and%20Queues/Stacks%20and%20Queues.html
FI: first in
FO: first out
LI: last in
LO: last out
you can do whatever viable combination that you want:
FIFO: first in first out
FILO: first in last out
LIFO: last in first out
LILO: last in last out
it gets a bit more complicated in its various applications, but the basic concept is really just to imagine you're storing a bunch of plates on top of each other, but you can only easily/safely remove either the top or bottom plate, as trying to remove a plate from the middle is too risky/dangerous of dropping/breaking the plates, and it's not easy to remove/add a plate out-of/into the middle of a stack of plates.
mrangel
25 Aug 2018, 11:20I was about to say that you can't easily construct a stack in Quest, due to the inability to remove elements from the end of a list.
But then I realised you could make a DLL easily enough, making it an object. And because it's an object, assigning it to an attribute wouldn't make a silent deep copy (whether this is a good thing or not is a matter of opinion).