ID:2541862
 
(See the best response by Lummox JR.)
Code:
        if(M.block_tele)
usr<< "You wait for an answer from [M]"
switch(alert(M,"[src] is trying to teleport to you! Allow [src] to come to you?","","Yes","No"))
if("Yes")
..()
if("No")
src<<"You can't sense [M]"
return


Problem description:
So the issue is if usr tries to teleport to M, it asks M if that's ok, which is what I want. Now if M is AFK and doesn't answer, I don't them to come back 2 hours later, hit yes, and usr is teleported. Is there a way to remove the switch proc from M after a certain amount of time?
You can measure world.time before and after the alert() call and bail out if the difference is too large.
Alright I'll look into that, thank you!
Best response
There's a really old and well-established way to put alert/input calls on a timer. You create a datum, and call a datum proc that spawns a del() of itself before calling alert/input. Although you can also do it with a global proc with some trickery. Something like this:

proc/timeout_alert(timeout, target, text, title, b1, b2, b3)
src = new/datum() // make this proc die if src dies
spawn(timeout) del(src)
if(b3) return alert(target, text, title, b1, b2, b3)
if(b2) return alert(target, text, title, b1, b2)
if(b1) return alert(target, text, title, b1)
return alert(target, text, title)

This works by tying the proc to an object. Any proc whose src value is deleted ends immediately, which is why you can make an object proc outlive its object by setting src=null. Prompts that are in a proc that ends prematurely are canceled.

From a UI perspective though, you're better off finding a prettier modern means of presenting input choices. The alert box is kind of ugly.
Well thank you for the suggestion, if all else fails then people can deal with the ugly alert box lol. Granted I can put that all together idk ^^
Given these quirks about DM:

1) alert() sleeps the calling proc until an answer is received.

2) procs can be stopped by deleting src.

3) You can change a proc's src at any time.

4) spawn()ing copies the local variable scope.


Given an understanding of these facts, we can create a very uniquely DM code structure that will break your brain in magical ways.


We need to understand how to do two things to rig up this weird behavior:

A) Cross-spawn communication:

var/list/l = list("herp")
spawn(10)
world.log << "spawn 1 says: l[1]"
l[1] = "derp"
spawn(10)
world.log << "spawn 2 says: [l[1]]"
l[1] = "herp"


We can communicate between calls by using a local variable if all three local variables reference the same object where we can store data. Since the local memory is copied between the spawn and the calling proc, we can use lists to pass data between spawned code and the calling code.

B) The timeout alert:

var/list/l = list("waiting")
var/expiration = world.time + 600
var/datum/d = new()
spawn()
var/ref = src
src = d
l[1] = alert(M,"[ref] is trying to teleport to you! Allow [ref] to come to you?","Yes","No")

//wait until one of the alerts has been accepted or has expired
while(l[1]=="waiting")
if(world.time > expiration)
del d
l[1] = "expired"
else
sleep(world.tick_lag)

//process the results of the request
switch(l[1])
if("Yes")
//teleport the player
if("No")
src << "[M] refuses to accept your teleport."
if("expired")
src << "[M] fails to accept your teleport."


Right, so this is a little complicated, but the gist is that we're spawning off a block of code while hanging on to a list we can use to communicate with it. We are using a datum that we created just to be the src for the spawned alert. If we delete this datum, the spawned alert goes too.


Finally, we can implement what I call a negotiated alert by making a few small changes:

var/list/l = list("waiting")
var/expiration = world.time + 600
var/datum/d = new()

//notify the other player
spawn()
var/ref = src
src = d
l[1] = alert(M,"[ref] is trying to teleport to you! Allow [ref] to come to you?","Yes","No")
//allow this player to cancel
spawn()
var/ref = src
src = d
l[1] = alert(ref,"You wait for an answer from [M]...","Cancel")

//wait until one of the alerts has been accepted or has expired
while(l[1]=="waiting")
if(world.time > expiration)
del d
l[1] = "expired"
else
sleep(world.tick_lag)

//process the results of the request
switch(l[1])
if("Yes")
//teleport the player
if("No")
src << "[M] refuses to accept your teleport."
if("expired")
src << "[M] fails to accept your teleport."
if("Cancel")
del d
M << "[src] has withdrawn their request."


The only major difference here is that we added a second slert that's tied to the same datum, so both players will know at roughly the same time that something happened. We also need to process the case where the teleporting player cancels, so we added another case under the processing switch.

I want to point out that I didn't test any of this. It's all theory based on what I know about DM. If it works, it works, and I should also warn you that it's a really ugly way to go about something like this, but the end result to the players should be much better overall.