mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-23 03:57:13 +01:00
Ports over Timer Subsystem
This commit is contained in:
@@ -32,6 +32,8 @@
|
||||
#define ToDegrees(radians) ((radians) * 57.2957795) // 180 / Pi
|
||||
#define ToRadians(degrees) ((degrees) * 0.0174532925) // Pi / 180
|
||||
|
||||
#define SHORT_REAL_LIMIT 16777216
|
||||
|
||||
//"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks
|
||||
//percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio)
|
||||
//collapsed to percent_of_tick_used * tick_lag
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b)
|
||||
return a.priority - b.priority
|
||||
|
||||
/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
|
||||
return a.timeToRun - b.timeToRun
|
||||
|
||||
/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
|
||||
. = B.hard_delete_time - A.hard_delete_time
|
||||
if(!.)
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
#define QDEL_IN(item, time) addtimer(GLOBAL_PROC, "qdel", time, FALSE, item)
|
||||
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE)
|
||||
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
|
||||
#define QDEL_NULL(item) if(item) { qdel(item); item = null }
|
||||
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
|
||||
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE)
|
||||
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
|
||||
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
|
||||
|
||||
/proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly.
|
||||
QDEL_LIST(L)
|
||||
@@ -44,7 +44,7 @@ DECLARE_GLOBAL_CONTROLLER(mob_hunt, mob_hunt_server)
|
||||
recover_time = 3000
|
||||
if(recover_time > 0) //when provided with a negative or zero valued recover_time argument, the server won't auto-restart but can be manually rebooted still
|
||||
//set a timer to automatically recover after recover_time has passed (can be manually restarted if you get impatient too)
|
||||
addtimer(src, "auto_recover", recover_time, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/auto_recover), recover_time, TIMER_UNIQUE)
|
||||
|
||||
/datum/controller/process/mob_hunt/proc/client_mob_update()
|
||||
var/list/ex_players = list()
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
var/global/datum/controller/process/timer/timer_master
|
||||
|
||||
/datum/controller/process/timer
|
||||
var/list/processing_timers = list()
|
||||
var/list/hashes = list()
|
||||
|
||||
/datum/controller/process/timer/setup()
|
||||
name = "timer"
|
||||
schedule_interval = 1 //every 0.1 seconds--2 server ticks
|
||||
log_startup_progress("Timer process starting up.")
|
||||
|
||||
/datum/controller/process/timer/statProcess()
|
||||
..()
|
||||
stat(null, "[processing_timers.len] timers")
|
||||
|
||||
/datum/controller/process/timer/doWork()
|
||||
if(!processing_timers.len)
|
||||
disabled = 1 //nothing to do, lets stop firing.
|
||||
return
|
||||
for(last_object in processing_timers)
|
||||
var/datum/timedevent/event = last_object
|
||||
if(!event.thingToCall || check_datum_qdeleted(event.thingToCall))
|
||||
qdel(event)
|
||||
if(event.timeToRun <= world.time)
|
||||
runevent(event)
|
||||
qdel(event)
|
||||
SCHECK
|
||||
|
||||
DECLARE_GLOBAL_CONTROLLER(timer, timer_master)
|
||||
|
||||
/datum/controller/process/timer/proc/runevent(datum/timedevent/event)
|
||||
set waitfor = 0
|
||||
if(event.thingToCall)
|
||||
if(event.thingToCall == GLOBAL_PROC && istext(event.procToCall))
|
||||
call("/proc/[event.procToCall]")(arglist(event.argList))
|
||||
else
|
||||
call(event.thingToCall, event.procToCall)(arglist(event.argList))
|
||||
|
||||
/datum/timedevent
|
||||
var/thingToCall
|
||||
var/procToCall
|
||||
var/timeToRun
|
||||
var/argList
|
||||
var/id
|
||||
var/hash
|
||||
var/static/nextid = 1
|
||||
|
||||
/datum/timedevent/New()
|
||||
id = nextid++
|
||||
|
||||
/datum/timedevent/Destroy()
|
||||
timer_master.processing_timers -= src
|
||||
timer_master.hashes -= hash
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/proc/addtimer(thingToCall, procToCall, wait, unique = FALSE, ...)
|
||||
if(!timer_master) //can't run timers before the mc has been created
|
||||
return
|
||||
if(!thingToCall || !procToCall)
|
||||
return
|
||||
if(timer_master.disabled)
|
||||
timer_master.disabled = 0
|
||||
|
||||
var/datum/timedevent/event = new()
|
||||
event.thingToCall = thingToCall
|
||||
event.procToCall = procToCall
|
||||
event.timeToRun = world.time + wait
|
||||
var/hashlist = args.Copy()
|
||||
|
||||
hashlist[1] = "[thingToCall](\ref[thingToCall])"
|
||||
event.hash = jointext(hashlist, null)
|
||||
if(args.len > 4)
|
||||
event.argList = args.Copy(5)
|
||||
|
||||
// Check for dupes if unique = 1.
|
||||
if(unique)
|
||||
var/datum/timedevent/hash_event = timer_master.hashes[event.hash]
|
||||
if(hash_event)
|
||||
return hash_event.id
|
||||
timer_master.hashes[event.hash] = event
|
||||
if(wait <= 0)
|
||||
timer_master.runevent(event)
|
||||
timer_master.hashes -= event.hash
|
||||
return
|
||||
// If we are unique (or we're not checking that), add the timer and return the id.
|
||||
timer_master.processing_timers += event
|
||||
|
||||
return event.id
|
||||
|
||||
/proc/deltimer(id)
|
||||
if(id == 0)
|
||||
// No event will correspond to an id of 0 - the timer does not exist
|
||||
// Save us a possibly expensive iteration through the timer list
|
||||
// This would probably be more efficient in general if we used an associative list instead
|
||||
return 0
|
||||
for(var/datum/timedevent/event in timer_master.processing_timers)
|
||||
if(event.id == id)
|
||||
qdel(event)
|
||||
return 1
|
||||
return 0
|
||||
@@ -37,7 +37,7 @@ var/global/datum/controller/process/weather/weather_master
|
||||
var/datum/weather/W = pickweight(possible_weather_for_this_z)
|
||||
run_weather(W.name)
|
||||
eligible_zlevels -= Z
|
||||
addtimer(src, "make_z_eligible", rand(3000, 6000) + W.weather_duration_upper, TRUE, Z) //Around 5-10 minutes between weathers
|
||||
addtimer(CALLBACK(src, .proc/make_z_eligible, Z), rand(3000, 6000) + W.weather_duration_upper, TIMER_UNIQUE) //Around 5-10 minutes between weathers
|
||||
|
||||
DECLARE_GLOBAL_CONTROLLER(weather, weather_master)
|
||||
|
||||
|
||||
@@ -0,0 +1,518 @@
|
||||
#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
|
||||
#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN) + 1)
|
||||
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
|
||||
#define TIMER_ID_MAX (2**24) //max float with integer precision
|
||||
|
||||
SUBSYSTEM_DEF(timer)
|
||||
name = "Timer"
|
||||
wait = 1 //SS_TICKER subsystem, so wait is in ticks
|
||||
init_order = INIT_ORDER_TIMER
|
||||
|
||||
flags = SS_TICKER|SS_NO_INIT
|
||||
|
||||
var/list/datum/timedevent/second_queue = list() //awe, yes, you've had first queue, but what about second queue?
|
||||
var/list/hashes = list()
|
||||
|
||||
var/head_offset = 0 //world.time of the first entry in the the bucket.
|
||||
var/practical_offset = 1 //index of the first non-empty item in the bucket.
|
||||
var/bucket_resolution = 0 //world.tick_lag the bucket was designed for
|
||||
var/bucket_count = 0 //how many timers are in the buckets
|
||||
|
||||
var/list/bucket_list = list() //list of buckets, each bucket holds every timer that has to run that byond tick.
|
||||
|
||||
var/list/timer_id_dict = list() //list of all active timers assoicated to their timer id (for easy lookup)
|
||||
|
||||
var/list/clienttime_timers = list() //special snowflake timers that run on fancy pansy "client time"
|
||||
|
||||
var/last_invoke_tick = 0
|
||||
var/static/last_invoke_warning = 0
|
||||
var/static/bucket_auto_reset = TRUE
|
||||
|
||||
/datum/controller/subsystem/timer/PreInit()
|
||||
bucket_list.len = BUCKET_LEN
|
||||
head_offset = world.time
|
||||
bucket_resolution = world.tick_lag
|
||||
|
||||
/datum/controller/subsystem/timer/stat_entry(msg)
|
||||
..("B:[bucket_count] P:[length(second_queue)] H:[length(hashes)] C:[length(clienttime_timers)] S:[length(timer_id_dict)]")
|
||||
|
||||
/datum/controller/subsystem/timer/fire(resumed = FALSE)
|
||||
var/lit = last_invoke_tick
|
||||
var/last_check = world.time - TIMER_NO_INVOKE_WARNING
|
||||
var/list/bucket_list = src.bucket_list
|
||||
|
||||
if(!bucket_count)
|
||||
last_invoke_tick = world.time
|
||||
|
||||
if(lit && lit < last_check && last_invoke_warning < last_check)
|
||||
last_invoke_warning = world.time
|
||||
var/msg = "No regular timers processed in the last [TIMER_NO_INVOKE_WARNING] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
|
||||
message_admins(msg)
|
||||
WARNING(msg)
|
||||
if(bucket_auto_reset)
|
||||
bucket_resolution = 0
|
||||
|
||||
log_world("Timer bucket reset. world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
for(var/i in 1 to length(bucket_list))
|
||||
var/datum/timedevent/bucket_head = bucket_list[i]
|
||||
if(!bucket_head)
|
||||
continue
|
||||
|
||||
log_world("Active timers at index [i]:")
|
||||
|
||||
var/datum/timedevent/bucket_node = bucket_head
|
||||
var/anti_loop_check = 1000
|
||||
do
|
||||
log_world(get_timer_debug_string(bucket_node))
|
||||
bucket_node = bucket_node.next
|
||||
anti_loop_check--
|
||||
while(bucket_node && bucket_node != bucket_head && anti_loop_check)
|
||||
log_world("Active timers in the second_queue queue:")
|
||||
for(var/I in second_queue)
|
||||
log_world(get_timer_debug_string(I))
|
||||
|
||||
var/next_clienttime_timer_index = 0
|
||||
var/len = length(clienttime_timers)
|
||||
|
||||
for(next_clienttime_timer_index in 1 to len)
|
||||
if(MC_TICK_CHECK)
|
||||
next_clienttime_timer_index--
|
||||
break
|
||||
var/datum/timedevent/ctime_timer = clienttime_timers[next_clienttime_timer_index]
|
||||
if(ctime_timer.timeToRun > REALTIMEOFDAY)
|
||||
next_clienttime_timer_index--
|
||||
break
|
||||
|
||||
var/datum/callback/callBack = ctime_timer.callBack
|
||||
if(!callBack)
|
||||
clienttime_timers.Cut(next_clienttime_timer_index,next_clienttime_timer_index+1)
|
||||
CRASH("Invalid timer: [get_timer_debug_string(ctime_timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset], REALTIMEOFDAY: [REALTIMEOFDAY]")
|
||||
|
||||
ctime_timer.spent = REALTIMEOFDAY
|
||||
callBack.InvokeAsync()
|
||||
qdel(ctime_timer)
|
||||
|
||||
|
||||
if(next_clienttime_timer_index)
|
||||
clienttime_timers.Cut(1,next_clienttime_timer_index+1)
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
var/static/list/spent = list()
|
||||
var/static/datum/timedevent/timer
|
||||
if(practical_offset > BUCKET_LEN)
|
||||
head_offset += TICKS2DS(BUCKET_LEN)
|
||||
practical_offset = 1
|
||||
resumed = FALSE
|
||||
|
||||
if((length(bucket_list) != BUCKET_LEN) || (world.tick_lag != bucket_resolution))
|
||||
reset_buckets()
|
||||
bucket_list = src.bucket_list
|
||||
resumed = FALSE
|
||||
|
||||
|
||||
if(!resumed)
|
||||
timer = null
|
||||
|
||||
while(practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time)
|
||||
var/datum/timedevent/head = bucket_list[practical_offset]
|
||||
if(!timer || !head || timer == head)
|
||||
head = bucket_list[practical_offset]
|
||||
timer = head
|
||||
while(timer)
|
||||
var/datum/callback/callBack = timer.callBack
|
||||
if(!callBack)
|
||||
bucket_resolution = null //force bucket recreation
|
||||
CRASH("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
|
||||
if(!timer.spent)
|
||||
spent += timer
|
||||
timer.spent = world.time
|
||||
callBack.InvokeAsync()
|
||||
last_invoke_tick = world.time
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
timer = timer.next
|
||||
if(timer == head)
|
||||
break
|
||||
|
||||
|
||||
bucket_list[practical_offset++] = null
|
||||
|
||||
//we freed up a bucket, lets see if anything in second_queue needs to be shifted to that bucket.
|
||||
var/i = 0
|
||||
var/L = length(second_queue)
|
||||
for(i in 1 to L)
|
||||
timer = second_queue[i]
|
||||
if(timer.timeToRun >= TIMER_MAX)
|
||||
i--
|
||||
break
|
||||
|
||||
if(timer.timeToRun < head_offset)
|
||||
bucket_resolution = null //force bucket recreation
|
||||
CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
|
||||
if(timer.callBack && !timer.spent)
|
||||
timer.callBack.InvokeAsync()
|
||||
spent += timer
|
||||
bucket_count++
|
||||
else if(!QDELETED(timer))
|
||||
qdel(timer)
|
||||
continue
|
||||
|
||||
if(timer.timeToRun < head_offset + TICKS2DS(practical_offset))
|
||||
bucket_resolution = null //force bucket recreation
|
||||
CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
if(timer.callBack && !timer.spent)
|
||||
timer.callBack.InvokeAsync()
|
||||
spent += timer
|
||||
bucket_count++
|
||||
else if(!QDELETED(timer))
|
||||
qdel(timer)
|
||||
continue
|
||||
|
||||
bucket_count++
|
||||
var/bucket_pos = max(1, BUCKET_POS(timer))
|
||||
|
||||
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
||||
if(!bucket_head)
|
||||
bucket_list[bucket_pos] = timer
|
||||
timer.next = null
|
||||
timer.prev = null
|
||||
continue
|
||||
|
||||
if(!bucket_head.prev)
|
||||
bucket_head.prev = bucket_head
|
||||
timer.next = bucket_head
|
||||
timer.prev = bucket_head.prev
|
||||
timer.next.prev = timer
|
||||
timer.prev.next = timer
|
||||
if(i)
|
||||
second_queue.Cut(1, i+1)
|
||||
|
||||
timer = null
|
||||
|
||||
bucket_count -= length(spent)
|
||||
|
||||
for(var/spent_timer in spent)
|
||||
qdel(spent_timer)
|
||||
|
||||
spent.len = 0
|
||||
|
||||
//formated this way to be runtime resistant
|
||||
/datum/controller/subsystem/timer/proc/get_timer_debug_string(datum/timedevent/TE)
|
||||
. = "Timer: [TE]"
|
||||
. += "Prev: [TE.prev ? TE.prev : "NULL"], Next: [TE.next ? TE.next : "NULL"]"
|
||||
if(TE.spent)
|
||||
. += ", SPENT([TE.spent])"
|
||||
if(QDELETED(TE))
|
||||
. += ", QDELETED"
|
||||
if(!TE.callBack)
|
||||
. += ", NO CALLBACK"
|
||||
|
||||
/datum/controller/subsystem/timer/proc/reset_buckets()
|
||||
var/list/bucket_list = src.bucket_list
|
||||
var/list/alltimers = list()
|
||||
//collect the timers currently in the bucket
|
||||
for(var/bucket_head in bucket_list)
|
||||
if(!bucket_head)
|
||||
continue
|
||||
var/datum/timedevent/bucket_node = bucket_head
|
||||
do
|
||||
alltimers += bucket_node
|
||||
bucket_node = bucket_node.next
|
||||
while(bucket_node && bucket_node != bucket_head)
|
||||
|
||||
bucket_list.len = 0
|
||||
bucket_list.len = BUCKET_LEN
|
||||
|
||||
practical_offset = 1
|
||||
bucket_count = 0
|
||||
head_offset = world.time
|
||||
bucket_resolution = world.tick_lag
|
||||
|
||||
alltimers += second_queue
|
||||
if(!length(alltimers))
|
||||
return
|
||||
|
||||
sortTim(alltimers, .proc/cmp_timer)
|
||||
|
||||
var/datum/timedevent/head = alltimers[1]
|
||||
|
||||
if(head.timeToRun < head_offset)
|
||||
head_offset = head.timeToRun
|
||||
|
||||
var/new_bucket_count
|
||||
var/i = 1
|
||||
for(i in 1 to length(alltimers))
|
||||
var/datum/timedevent/timer = alltimers[1]
|
||||
if(!timer)
|
||||
continue
|
||||
|
||||
var/bucket_pos = BUCKET_POS(timer)
|
||||
if(timer.timeToRun >= TIMER_MAX)
|
||||
i--
|
||||
break
|
||||
|
||||
|
||||
if(!timer.callBack || timer.spent)
|
||||
WARNING("Invalid timer: [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
|
||||
if(timer.callBack)
|
||||
qdel(timer)
|
||||
continue
|
||||
|
||||
new_bucket_count++
|
||||
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
||||
if(!bucket_head)
|
||||
bucket_list[bucket_pos] = timer
|
||||
timer.next = null
|
||||
timer.prev = null
|
||||
continue
|
||||
|
||||
if(!bucket_head.prev)
|
||||
bucket_head.prev = bucket_head
|
||||
timer.next = bucket_head
|
||||
timer.prev = bucket_head.prev
|
||||
timer.next.prev = timer
|
||||
timer.prev.next = timer
|
||||
if(i)
|
||||
alltimers.Cut(1, i+1)
|
||||
second_queue = alltimers
|
||||
bucket_count = new_bucket_count
|
||||
|
||||
|
||||
/datum/controller/subsystem/timer/Recover()
|
||||
second_queue |= SStimer.second_queue
|
||||
hashes |= SStimer.hashes
|
||||
timer_id_dict |= SStimer.timer_id_dict
|
||||
bucket_list |= SStimer.bucket_list
|
||||
|
||||
/datum/timedevent
|
||||
var/id
|
||||
var/datum/callback/callBack
|
||||
var/timeToRun
|
||||
var/hash
|
||||
var/list/flags
|
||||
var/spent = 0 //time we ran the timer.
|
||||
var/name //for easy debugging.
|
||||
//cicular doublely linked list
|
||||
var/datum/timedevent/next
|
||||
var/datum/timedevent/prev
|
||||
|
||||
/datum/timedevent/New(datum/callback/callBack, timeToRun, flags, hash)
|
||||
var/static/nextid = 1
|
||||
id = TIMER_ID_NULL
|
||||
src.callBack = callBack
|
||||
src.timeToRun = timeToRun
|
||||
src.flags = flags
|
||||
src.hash = hash
|
||||
|
||||
if(flags & TIMER_UNIQUE)
|
||||
SStimer.hashes[hash] = src
|
||||
|
||||
if(flags & TIMER_STOPPABLE)
|
||||
id = num2text(nextid, 100)
|
||||
if(nextid >= SHORT_REAL_LIMIT)
|
||||
nextid += min(1, 2**round(nextid/SHORT_REAL_LIMIT))
|
||||
else
|
||||
nextid++
|
||||
SStimer.timer_id_dict[id] = src
|
||||
|
||||
name = "Timer: [id] (\ref[src]), TTR: [timeToRun], Flags: [jointext(bitfield2list(flags, list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT")), ", ")], callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""])"
|
||||
|
||||
if((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME))
|
||||
CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]")
|
||||
|
||||
if(callBack.object != GLOBAL_PROC)
|
||||
LAZYADD(callBack.object.active_timers, src)
|
||||
|
||||
|
||||
var/list/L
|
||||
|
||||
if(flags & TIMER_CLIENT_TIME)
|
||||
L = SStimer.clienttime_timers
|
||||
else if(timeToRun >= TIMER_MAX)
|
||||
L = SStimer.second_queue
|
||||
|
||||
|
||||
if(L)
|
||||
//binary search sorted insert
|
||||
var/cttl = length(L)
|
||||
if(cttl)
|
||||
var/left = 1
|
||||
var/right = cttl
|
||||
var/mid = (left+right) >> 1 //rounded divide by two for hedgehogs
|
||||
|
||||
var/datum/timedevent/item
|
||||
while(left < right)
|
||||
item = L[mid]
|
||||
if(item.timeToRun <= timeToRun)
|
||||
left = mid+1
|
||||
else
|
||||
right = mid
|
||||
mid = (left+right) >> 1
|
||||
|
||||
item = L[mid]
|
||||
mid = item.timeToRun > timeToRun ? mid : mid+1
|
||||
L.Insert(mid, src)
|
||||
|
||||
else
|
||||
L += src
|
||||
return
|
||||
|
||||
//get the list of buckets
|
||||
var/list/bucket_list = SStimer.bucket_list
|
||||
|
||||
//calculate our place in the bucket list
|
||||
var/bucket_pos = BUCKET_POS(src)
|
||||
|
||||
//get the bucket for our tick
|
||||
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
||||
SStimer.bucket_count++
|
||||
//empty bucket, we will just add ourselves
|
||||
if(!bucket_head)
|
||||
bucket_list[bucket_pos] = src
|
||||
return
|
||||
//other wise, lets do a simplified linked list add.
|
||||
if(!bucket_head.prev)
|
||||
bucket_head.prev = bucket_head
|
||||
next = bucket_head
|
||||
prev = bucket_head.prev
|
||||
next.prev = src
|
||||
prev.next = src
|
||||
|
||||
/datum/timedevent/Destroy()
|
||||
..()
|
||||
if(flags & TIMER_UNIQUE && hash)
|
||||
SStimer.hashes -= hash
|
||||
|
||||
if(callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers)
|
||||
callBack.object.active_timers -= src
|
||||
UNSETEMPTY(callBack.object.active_timers)
|
||||
|
||||
callBack = null
|
||||
|
||||
if(flags & TIMER_STOPPABLE)
|
||||
SStimer.timer_id_dict -= id
|
||||
|
||||
if(flags & TIMER_CLIENT_TIME)
|
||||
if(!spent)
|
||||
spent = world.time
|
||||
SStimer.clienttime_timers -= src
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
if(!spent)
|
||||
spent = world.time
|
||||
var/bucketpos = BUCKET_POS(src)
|
||||
var/datum/timedevent/buckethead
|
||||
var/list/bucket_list = SStimer.bucket_list
|
||||
if(bucketpos > 0)
|
||||
buckethead = bucket_list[bucketpos]
|
||||
|
||||
if(buckethead == src)
|
||||
bucket_list[bucketpos] = next
|
||||
SStimer.bucket_count--
|
||||
else if(timeToRun < TIMER_MAX || next || prev)
|
||||
SStimer.bucket_count--
|
||||
else
|
||||
var/l = length(SStimer.second_queue)
|
||||
SStimer.second_queue -= src
|
||||
if(l == length(SStimer.second_queue))
|
||||
SStimer.bucket_count--
|
||||
|
||||
if(prev == next && next)
|
||||
next.prev = null
|
||||
prev.next = null
|
||||
else
|
||||
if(prev)
|
||||
prev.next = next
|
||||
if(next)
|
||||
next.prev = prev
|
||||
else
|
||||
if(prev && prev.next == src)
|
||||
prev.next = next
|
||||
if(next && next.prev == src)
|
||||
next.prev = prev
|
||||
next = null
|
||||
prev = null
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/datum/timedevent/proc/getcallingtype()
|
||||
. = "ERROR"
|
||||
if(callBack.object == GLOBAL_PROC)
|
||||
. = "GLOBAL_PROC"
|
||||
else
|
||||
. = "[callBack.object.type]"
|
||||
|
||||
/proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
|
||||
if(!callback)
|
||||
CRASH("addtimer called without a callback")
|
||||
|
||||
if(wait < 0)
|
||||
stack_trace("addtimer called with a negative wait. Converting to 0")
|
||||
|
||||
//alot of things add short timers on themselves in their destroy, we ignore those cases
|
||||
if(wait >= 1 && callback && callback.object && callback.object != GLOBAL_PROC && QDELETED(callback.object))
|
||||
stack_trace("addtimer called with a callback assigned to a qdeleted object")
|
||||
|
||||
wait = max(wait, 0)
|
||||
|
||||
if(wait >= INFINITY)
|
||||
CRASH("Attempted to create timer with INFINITY delay")
|
||||
|
||||
var/hash
|
||||
|
||||
if(flags & TIMER_UNIQUE)
|
||||
var/list/hashlist
|
||||
if(flags & TIMER_NO_HASH_WAIT)
|
||||
hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, flags & TIMER_CLIENT_TIME)
|
||||
else
|
||||
hashlist = list(callback.object, "([callback.object.UID()])", callback.delegate, wait, flags & TIMER_CLIENT_TIME)
|
||||
hashlist += callback.arguments
|
||||
hash = hashlist.Join("|||||||")
|
||||
|
||||
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
|
||||
if(hash_timer)
|
||||
if(hash_timer.spent) //it's pending deletion, pretend it doesn't exist.
|
||||
hash_timer.hash = null //but keep it from accidentally deleting us
|
||||
else
|
||||
if(flags & TIMER_OVERRIDE)
|
||||
hash_timer.hash = null //no need having it delete it's hash if we are going to replace it
|
||||
qdel(hash_timer)
|
||||
else
|
||||
if(hash_timer.flags & TIMER_STOPPABLE)
|
||||
. = hash_timer.id
|
||||
return
|
||||
|
||||
|
||||
var/timeToRun = world.time + wait
|
||||
if(flags & TIMER_CLIENT_TIME)
|
||||
timeToRun = REALTIMEOFDAY + wait
|
||||
|
||||
var/datum/timedevent/timer = new(callback, timeToRun, flags, hash)
|
||||
return timer.id
|
||||
|
||||
/proc/deltimer(id)
|
||||
if(!id)
|
||||
return FALSE
|
||||
if(id == TIMER_ID_NULL)
|
||||
CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag")
|
||||
if(!istext(id))
|
||||
if(istype(id, /datum/timedevent))
|
||||
qdel(id)
|
||||
return TRUE
|
||||
//id is string
|
||||
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
|
||||
if(timer && !timer.spent)
|
||||
qdel(timer)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
#undef BUCKET_LEN
|
||||
#undef BUCKET_POS
|
||||
#undef TIMER_MAX
|
||||
#undef TIMER_ID_MAX
|
||||
@@ -91,7 +91,7 @@
|
||||
debug_variables(shuttle_master)
|
||||
feedback_add_details("admin_verb","DShuttle")
|
||||
if("Timer")
|
||||
debug_variables(timer_master)
|
||||
debug_variables(SStimer)
|
||||
feedback_add_details("admin_verb","DTimer")
|
||||
if("Weather")
|
||||
debug_variables(weather_master)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/datum
|
||||
var/gc_destroyed //Time when this object was destroyed.
|
||||
var/list/active_timers //for SStimer
|
||||
var/list/datum_components //for /datum/components
|
||||
var/var_edited = FALSE //Warranty void if seal is broken
|
||||
|
||||
@@ -16,6 +17,14 @@
|
||||
/datum/proc/Destroy(force = FALSE, ...)
|
||||
tag = null
|
||||
|
||||
var/list/timers = active_timers
|
||||
active_timers = null
|
||||
for(var/thing in timers)
|
||||
var/datum/timedevent/timer = thing
|
||||
if (timer.spent)
|
||||
continue
|
||||
qdel(timer)
|
||||
|
||||
var/list/dc = datum_components
|
||||
if(dc)
|
||||
var/all_components = dc[/datum/component]
|
||||
|
||||
+1
-2
@@ -1538,8 +1538,7 @@
|
||||
H.update_inv_w_uniform(0,0)
|
||||
|
||||
add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes")
|
||||
addtimer(src, "remove_zealot", convert_duration, FALSE, jumpsuit) //deconverts after the timer expires
|
||||
|
||||
addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires
|
||||
return 1
|
||||
|
||||
/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null)
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains
|
||||
to_chat(target, "<span class='warning'>You are somehow too bound to your current location to abandon it.</span>")
|
||||
continue
|
||||
addtimer(src, "do_jaunt", 0, FALSE, target)
|
||||
INVOKE_ASYNC(src, .proc/do_jaunt, target)
|
||||
|
||||
/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target)
|
||||
target.notransform = 1
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins
|
||||
var/target_z = MAIN_STATION //The z-level to affect
|
||||
var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas.
|
||||
|
||||
|
||||
var/overlay_layer = 10 //Since it's above everything else, this is the layer used by default. 2 is below mobs and walls if you need to use that.
|
||||
var/aesthetic = FALSE //If the weather has no purpose other than looks
|
||||
var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather
|
||||
@@ -70,7 +70,7 @@
|
||||
to_chat(M, telegraph_message)
|
||||
if(telegraph_sound)
|
||||
M << sound(telegraph_sound)
|
||||
addtimer(src, "start", telegraph_duration)
|
||||
addtimer(CALLBACK(src, .proc/start), telegraph_duration)
|
||||
|
||||
/datum/weather/proc/start()
|
||||
if(stage >= MAIN_STAGE)
|
||||
@@ -85,7 +85,7 @@
|
||||
if(weather_sound)
|
||||
M << sound(weather_sound)
|
||||
weather_master.processing_weather |= src
|
||||
addtimer(src, "wind_down", weather_duration)
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
|
||||
/datum/weather/proc/wind_down()
|
||||
if(stage >= WIND_DOWN_STAGE)
|
||||
@@ -100,7 +100,7 @@
|
||||
if(end_sound)
|
||||
M << sound(end_sound)
|
||||
weather_master.processing_weather -= src
|
||||
addtimer(src, "end", end_duration)
|
||||
addtimer(CALLBACK(src, .proc/end), end_duration)
|
||||
|
||||
/datum/weather/proc/end()
|
||||
if(stage == END_STAGE)
|
||||
|
||||
+1
-1
@@ -242,7 +242,7 @@
|
||||
|
||||
/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
|
||||
if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
|
||||
addtimer(src, "hitby_react", 2, TRUE, AM)
|
||||
addtimer(CALLBACK(src, .proc/hitby_react, AM), 2)
|
||||
|
||||
/atom/proc/hitby_react(atom/movable/AM)
|
||||
if(AM && isturf(AM.loc))
|
||||
|
||||
@@ -176,11 +176,11 @@
|
||||
|
||||
/mob/living/forceMove(atom/destination)
|
||||
if(buckled)
|
||||
addtimer(src, "check_buckled", 1, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE)
|
||||
if(buckled_mob)
|
||||
addtimer(buckled_mob, "check_buckled", 1, TRUE)
|
||||
addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE)
|
||||
if(pulling)
|
||||
addtimer(src, "check_pull", 1, TRUE)
|
||||
addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE)
|
||||
. = ..()
|
||||
if(client)
|
||||
reset_perspective(destination)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
return FALSE
|
||||
user.visible_message("<span class='warning'>[user] vomits a glob of acid on \his [O]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our restraints!</span>")
|
||||
addtimer(src, "dissolve_handcuffs", 30, FALSE, user, O)
|
||||
addtimer(CALLBACK(src, .proc/dissolve_handcuffs, user, O), 30)
|
||||
used = TRUE
|
||||
|
||||
if(user.wear_suit && user.wear_suit.breakouttime && !used)
|
||||
@@ -27,7 +27,7 @@
|
||||
return FALSE
|
||||
user.visible_message("<span class='warning'>[user] vomits a glob of acid across the front of \his [S]!</span>", \
|
||||
"<span class='warning'>We vomit acidic ooze onto our straight jacket!</span>")
|
||||
addtimer(src, "dissolve_straightjacket", 30, FALSE, user, S)
|
||||
addtimer(CALLBACK(src, .proc/dissolve_straightjacket, user, S), 30)
|
||||
used = TRUE
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
return FALSE
|
||||
C.visible_message("<span class='warning'>[C]'s hinges suddenly begin to melt and run!</span>")
|
||||
to_chat(user, "<span class='warning'>We vomit acidic goop onto the interior of [C]!</span>")
|
||||
addtimer(src, "open_closet", 70, FALSE, user, C)
|
||||
addtimer(CALLBACK(src, .proc/open_closet, user, C), 70)
|
||||
used = TRUE
|
||||
|
||||
if(istype(user.loc, /obj/structure/spider/cocoon) && !used)
|
||||
@@ -46,7 +46,7 @@
|
||||
return FALSE
|
||||
C.visible_message("<span class='warning'>[src] shifts and starts to fall apart!</span>")
|
||||
to_chat(user, "<span class='warning'>We secrete acidic enzymes from our skin and begin melting our cocoon...</span>")
|
||||
addtimer(src, "dissolve_cocoon", 25, FALSE, user, C) //Very short because it's just webs
|
||||
addtimer(CALLBACK(src, .proc/dissolve_cocoon, user, C), 25) //Very short because it's just webs
|
||||
used = TRUE
|
||||
|
||||
if(used)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
user.status_flags |= FAKEDEATH //play dead
|
||||
user.update_canmove()
|
||||
|
||||
addtimer(src, "ready_to_regenerate", LING_FAKEDEATH_TIME, FALSE, user)
|
||||
addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME)
|
||||
feedback_add_details("changeling_powers","FD")
|
||||
return 1
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
by quick repeated use!</span>")
|
||||
|
||||
recent_uses++
|
||||
addtimer(src, "fleshmend", 0, FALSE, user)
|
||||
INVOKE_ASYNC(src, .proc/fleshmend, user)
|
||||
feedback_add_details("changeling_powers","RR")
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -159,9 +159,8 @@
|
||||
for(var/obj/machinery/door/D in airlocks)
|
||||
if(!is_station_level(D.z))
|
||||
continue
|
||||
spawn(0)
|
||||
D.hostile_lockdown(src)
|
||||
addtimer(D, "disable_lockdown", 900)
|
||||
INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, src)
|
||||
addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900)
|
||||
|
||||
post_status("alert", "lockdown")
|
||||
|
||||
|
||||
@@ -43,7 +43,8 @@
|
||||
to_chat(B.host, "<span class='danger'>You feel the captive mind of [src] begin to resist your control.</span>")
|
||||
|
||||
var/delay = (rand(350,450) + B.host.brainloss)
|
||||
addtimer(src, "return_control", delay, FALSE, B)
|
||||
addtimer(CALLBACK(src, .proc/return_control, B), delay)
|
||||
|
||||
|
||||
/mob/living/captive_brain/proc/return_control(mob/living/simple_animal/borer/B)
|
||||
if(!B || !B.controlling)
|
||||
@@ -534,7 +535,7 @@
|
||||
|
||||
leaving = TRUE
|
||||
|
||||
addtimer(src, "let_go", 200)
|
||||
addtimer(CALLBACK(src, .proc/let_go), 200)
|
||||
|
||||
/mob/living/simple_animal/borer/proc/let_go()
|
||||
|
||||
@@ -611,7 +612,7 @@
|
||||
bonding = TRUE
|
||||
|
||||
var/delay = 300+(host.getBrainLoss()*5)
|
||||
addtimer(src, "assume_control", delay)
|
||||
addtimer(CALLBACK(src, .proc/assume_control), delay)
|
||||
|
||||
/mob/living/simple_animal/borer/proc/assume_control()
|
||||
if(!host || !src || controlling)
|
||||
|
||||
@@ -118,7 +118,7 @@ About the new airlock wires panel:
|
||||
/obj/machinery/door/airlock/initialize()
|
||||
. = ..()
|
||||
if(closeOtherId != null)
|
||||
addtimer(src, "update_other_id", 5)
|
||||
addtimer(CALLBACK(src, .proc/update_other_id), 5)
|
||||
if(glass)
|
||||
airlock_material = "glass"
|
||||
if(security_level > AIRLOCK_SECURITY_METAL)
|
||||
@@ -226,13 +226,11 @@ About the new airlock wires panel:
|
||||
/obj/machinery/door/airlock/proc/loseMainPower()
|
||||
main_power_lost_until = mainPowerCablesCut() ? -1 : world.time + SecondsToTicks(60)
|
||||
if(main_power_lost_until > 0)
|
||||
main_power_timer = addtimer(src, "regainMainPower", SecondsToTicks(60), 1)
|
||||
|
||||
main_power_timer = addtimer(CALLBACK(src, .proc/regainMainPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
// If backup power is permanently disabled then activate in 10 seconds if possible, otherwise it's already enabled or a timer is already running
|
||||
if(backup_power_lost_until == -1 && !backupPowerCablesCut())
|
||||
backup_power_lost_until = world.time + SecondsToTicks(10)
|
||||
backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(10), 1)
|
||||
|
||||
backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(10), TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
// Disable electricity if required
|
||||
if(electrified_until && isAllPowerLoss())
|
||||
electrify(0)
|
||||
@@ -240,7 +238,7 @@ About the new airlock wires panel:
|
||||
/obj/machinery/door/airlock/proc/loseBackupPower()
|
||||
backup_power_lost_until = backupPowerCablesCut() ? -1 : world.time + SecondsToTicks(60)
|
||||
if(backup_power_lost_until > 0)
|
||||
backup_power_timer = addtimer(src, "regainBackupPower", SecondsToTicks(60), 1)
|
||||
backup_power_timer = addtimer(CALLBACK(src, .proc/regainBackupPower), SecondsToTicks(60), TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
|
||||
// Disable electricity if required
|
||||
if(electrified_until && isAllPowerLoss())
|
||||
@@ -288,8 +286,7 @@ About the new airlock wires panel:
|
||||
message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]."
|
||||
electrified_until = duration == -1 ? -1 : world.time + SecondsToTicks(duration)
|
||||
if(duration != -1)
|
||||
electrified_timer = addtimer(src, "electrify", SecondsToTicks(duration), 1, 0)
|
||||
|
||||
electrified_timer = addtimer(CALLBACK(src, .proc/electrify, 0), SecondsToTicks(duration), TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
if(feedback && message)
|
||||
to_chat(usr, message)
|
||||
|
||||
@@ -1080,8 +1077,7 @@ About the new airlock wires panel:
|
||||
|
||||
// The `addtimer` system has the advantage of being cancelable
|
||||
if(autoclose)
|
||||
autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1)
|
||||
|
||||
autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/door/airlock/close(forced=0, override = 0)
|
||||
@@ -1098,7 +1094,7 @@ About the new airlock wires panel:
|
||||
for(var/turf/turf in locs)
|
||||
for(var/atom/movable/M in turf)
|
||||
if(M.density && M != src) //something is blocking the door
|
||||
addtimer(src, "autoclose", 60)
|
||||
addtimer(CALLBACK(src, .proc/autoclose), 60)
|
||||
|
||||
use_power(360) //360 W seems much more appropriate for an actuator moving an industrial door capable of crushing people
|
||||
if(forced)
|
||||
|
||||
@@ -150,13 +150,15 @@
|
||||
|
||||
/obj/machinery/door/airlock/uranium/New()
|
||||
..()
|
||||
addtimer(src, "radiate", event_step)
|
||||
addtimer(CALLBACK(src, .proc/radiate), event_step)
|
||||
|
||||
|
||||
/obj/machinery/door/airlock/uranium/proc/radiate()
|
||||
if(prob(50))
|
||||
for(var/mob/living/L in range (3,src))
|
||||
L.apply_effect(15,IRRADIATE,0)
|
||||
addtimer(src, "radiate", event_step)
|
||||
addtimer(CALLBACK(src, .proc/radiate), event_step)
|
||||
|
||||
|
||||
/obj/machinery/door/airlock/uranium/glass
|
||||
opacity = 0
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
|
||||
// The `addtimer` system has the advantage of being cancelable
|
||||
if(autoclose)
|
||||
autoclose_timer = addtimer(src, "autoclose", normalspeed ? auto_close_time : auto_close_time_dangerous, unique = 1)
|
||||
autoclose_timer = addtimer(CALLBACK(src, .proc/autoclose), normalspeed ? auto_close_time : auto_close_time_dangerous, TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
|
||||
return TRUE
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
for(var/atom/movable/M in get_turf(src))
|
||||
if(M.density && M != src) //something is blocking the door
|
||||
if(autoclose)
|
||||
addtimer(src, "autoclose", 60)
|
||||
addtimer(CALLBACK(src, .proc/autoclose), 60)
|
||||
return
|
||||
|
||||
operating = TRUE
|
||||
|
||||
@@ -143,7 +143,7 @@ var/const/SAFETY_COOLDOWN = 100
|
||||
safety_mode = 1
|
||||
update_icon()
|
||||
L.loc = loc
|
||||
addtimer(src, "reboot", SAFETY_COOLDOWN)
|
||||
addtimer(CALLBACK(src, .proc/reboot), SAFETY_COOLDOWN)
|
||||
|
||||
/obj/machinery/recycler/proc/reboot()
|
||||
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
|
||||
|
||||
@@ -438,7 +438,7 @@
|
||||
chem_splash(get_turf(src), spread_range, list(reactants), temp_boost)
|
||||
|
||||
// Detonate it again in one second, until it's out of juice.
|
||||
addtimer(src, "detonate", 10)
|
||||
addtimer(CALLBACK(src, .proc/detonate), 10)
|
||||
|
||||
// If it's not a time release bomb, do normal explosion
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
/obj/item/mecha_parts/mecha_equipment/proc/start_cooldown()
|
||||
set_ready_state(0)
|
||||
chassis.use_power(energy_drain)
|
||||
addtimer(src, "set_ready_state", equip_cooldown, FALSE, 1)
|
||||
addtimer(CALLBACK(src, .proc/set_ready_state, 1), equip_cooldown)
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/proc/do_after_cooldown(atom/target)
|
||||
if(!chassis)
|
||||
|
||||
@@ -44,7 +44,7 @@ var/global/list/image/splatter_cache=list()
|
||||
if(B.blood_DNA)
|
||||
blood_DNA |= B.blood_DNA.Copy()
|
||||
qdel(B)
|
||||
dry_timer = addtimer(src, "dry", DRYING_TIME * (amount+1))
|
||||
dry_timer = addtimer(CALLBACK(src, .proc/dry), DRYING_TIME * (amount+1), TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/decal/cleanable/blood/Destroy()
|
||||
if(GAMEMODE_IS_CULT)
|
||||
|
||||
@@ -51,7 +51,7 @@ would spawn and follow the beaker, even if it is carried or thrown.
|
||||
for(var/i in 1 to number)
|
||||
if(total_effects > 20)
|
||||
return
|
||||
addtimer(src, "generate_effect", 0)
|
||||
INVOKE_ASYNC(src, .proc/generate_effect)
|
||||
|
||||
/datum/effect_system/proc/generate_effect()
|
||||
if(holder)
|
||||
@@ -67,7 +67,7 @@ would spawn and follow the beaker, even if it is carried or thrown.
|
||||
for(var/j in 1 to steps_amt)
|
||||
sleep(5)
|
||||
step(E,direction)
|
||||
addtimer(src, "decrement_total_effect", 20)
|
||||
addtimer(CALLBACK(src, .proc/decrement_total_effect), 20)
|
||||
|
||||
/datum/effect_system/proc/decrement_total_effect()
|
||||
total_effects--
|
||||
|
||||
@@ -58,4 +58,4 @@
|
||||
|
||||
/datum/effect_system/explosion/smoke/start()
|
||||
..()
|
||||
addtimer(src, "create_smoke", 5)
|
||||
addtimer(CALLBACK(src, .proc/create_smoke), 5)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/kill_smoke()
|
||||
processing_objects.Remove(src)
|
||||
addtimer(src, "fade_out", 0)
|
||||
INVOKE_ASYNC(src, .proc/fade_out)
|
||||
QDEL_IN(src, 10)
|
||||
|
||||
/obj/effect/particle_effect/smoke/process()
|
||||
@@ -67,7 +67,7 @@
|
||||
if(C.smoke_delay)
|
||||
return FALSE
|
||||
C.smoke_delay++
|
||||
addtimer(src, "remove_smoke_delay", 10, FALSE, C)
|
||||
addtimer(CALLBACK(src, .proc/remove_smoke_delay, C), 10)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/particle_effect/smoke/proc/remove_smoke_delay(mob/living/carbon/C)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
else //if on the floor, glowshroom on-floor sprite
|
||||
icon_state = "[base_icon_state]f"
|
||||
|
||||
addtimer(src, "Spread", delay, FALSE)
|
||||
addtimer(CALLBACK(src, .proc/Spread), delay)
|
||||
|
||||
/obj/structure/glowshroom/proc/Spread()
|
||||
var/turf/ownturf = get_turf(src)
|
||||
@@ -117,7 +117,7 @@
|
||||
shrooms_planted++ //if we failed due to generation, don't try to plant one later
|
||||
if(shrooms_planted < myseed.yield) //if we didn't get all possible shrooms planted, try again later
|
||||
myseed.yield -= shrooms_planted
|
||||
addtimer(src, "Spread", delay, FALSE)
|
||||
addtimer(CALLBACK(src, .proc/Spread), delay)
|
||||
|
||||
/obj/structure/glowshroom/proc/CalcDir(turf/location = loc)
|
||||
var/direction = 16
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
to_chat(I.owner, "<span class='warning'>Your photon projector implant overheats and deactivates!</span>")
|
||||
I.Retract()
|
||||
overheat = FALSE
|
||||
addtimer(src, "cooldown", flashcd * 2)
|
||||
addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2)
|
||||
|
||||
/obj/item/flash/armimplant/try_use_flash(mob/user = null)
|
||||
if(overheat)
|
||||
@@ -216,7 +216,7 @@
|
||||
to_chat(I.owner, "<span class='warning'>Your photon projector is running too hot to be used again so quickly!</span>")
|
||||
return FALSE
|
||||
overheat = TRUE
|
||||
addtimer(src, "cooldown", flashcd)
|
||||
addtimer(CALLBACK(src, .proc/cooldown), flashcd)
|
||||
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
|
||||
update_icon(1)
|
||||
return TRUE
|
||||
|
||||
@@ -270,7 +270,7 @@ var/global/list/default_medbay_channels = list(
|
||||
universal_speak = 1
|
||||
|
||||
/mob/living/automatedannouncer/New()
|
||||
lifetime_timer = addtimer(src, "autocleanup", SecondsToTicks(10))
|
||||
lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE)
|
||||
..()
|
||||
|
||||
/mob/living/automatedannouncer/Destroy()
|
||||
@@ -619,7 +619,7 @@ var/global/list/default_medbay_channels = list(
|
||||
/obj/item/radio/emp_act(severity)
|
||||
on = 0
|
||||
disable_timer++
|
||||
addtimer(src, "enable_radio", rand(100, 200))
|
||||
addtimer(CALLBACK(src, .proc/enable_radio), rand(100, 200))
|
||||
|
||||
if(listening)
|
||||
visible_message("<span class='warning'>[src] buzzes violently!</span>")
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
|
||||
/obj/effect/decal/cleanable/ash/snappop_phoenix/New()
|
||||
. = ..()
|
||||
addtimer(src, "respawn", respawn_time)
|
||||
addtimer(CALLBACK(src, .proc/respawn), respawn_time)
|
||||
|
||||
/obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn()
|
||||
new /obj/item/toy/snappop/phoenix(get_turf(src))
|
||||
|
||||
@@ -385,7 +385,7 @@ RCD
|
||||
buzz loudly!</b></span>","<span class='danger'><b>[src] begins \
|
||||
vibrating violently!</b></span>")
|
||||
// 5 seconds to get rid of it
|
||||
addtimer(src, "detonate_pulse_explode", 50)
|
||||
addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50)
|
||||
|
||||
/obj/item/rcd/proc/detonate_pulse_explode()
|
||||
explosion(src, 0, 0, 3, 1, flame_range = 1)
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
target.overlays += image_overlay
|
||||
if(!nadeassembly)
|
||||
to_chat(user, "<span class='notice'>You plant the bomb. Timer counting down from [det_time].</span>")
|
||||
addtimer(src, "prime", det_time*10)
|
||||
addtimer(CALLBACK(src, .proc/prime), det_time*10)
|
||||
|
||||
/obj/item/grenade/plastic/suicide_act(mob/user)
|
||||
message_admins("[key_name_admin(user)](<A HREF='?_src_=holder;adminmoreinfo=\ref[user]'>?</A>) (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[user]'>FLW</A>) suicided with [src.name] at ([user.x],[user.y],[user.z] - <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)",0,1)
|
||||
|
||||
@@ -422,7 +422,7 @@
|
||||
message_admins("grenade primed by an assembly, attached by [key_name_admin(M)]<A HREF='?_src_=holder;adminmoreinfo=\ref[M]'>(?)</A> (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[M]'>FLW</A>) and last touched by [key_name_admin(last)]<A HREF='?_src_=holder;adminmoreinfo=\ref[last]'>(?)</A> (<A HREF='?_src_=holder;adminplayerobservefollow=\ref[last]'>FLW</A>) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[T.x];Y=[T.y];Z=[T.z]'>[A.name] (JMP)</a>.")
|
||||
log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])")
|
||||
else
|
||||
addtimer(src, "prime", det_time)
|
||||
addtimer(CALLBACK(src, .proc/prime), det_time)
|
||||
var/turf/DT = get_turf(src)
|
||||
var/area/DA = get_area(DT)
|
||||
log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])")
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/C = user
|
||||
C.throw_mode_on()
|
||||
addtimer(src, "prime", det_time)
|
||||
addtimer(CALLBACK(src, .proc/prime), det_time)
|
||||
|
||||
/obj/item/grenade/iedcasing/prime() //Blowing that can up
|
||||
update_mob()
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/energy/New()
|
||||
..()
|
||||
addtimer(src, "dissipate", 100)
|
||||
addtimer(CALLBACK(src, .proc/dissipate), 100)
|
||||
|
||||
/obj/item/restraints/legcuffs/beartrap/energy/proc/dissipate()
|
||||
if(!ismob(loc))
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
being_shocked = TRUE
|
||||
var/power_bounced = power * 0.5
|
||||
tesla_zap(src, 3, power_bounced)
|
||||
addtimer(src, "reset_shocked", 10)
|
||||
addtimer(CALLBACK(src, .proc/reset_shocked), 10)
|
||||
|
||||
/obj/proc/reset_shocked()
|
||||
being_shocked = FALSE
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
to_chat(user, "<span class='warning'>Nothing interesting happens!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You emag the barsign. Takeover in progress...</span>")
|
||||
addtimer(src, "post_emag", 100)
|
||||
addtimer(CALLBACK(src, .proc/post_emag), 100)
|
||||
|
||||
/obj/structure/sign/barsign/proc/post_emag()
|
||||
if(broken || emagged)
|
||||
|
||||
@@ -376,7 +376,7 @@
|
||||
return
|
||||
// Don't break if they're just flying past
|
||||
if(AM.throwing)
|
||||
addtimer(src, "throw_check", 5, FALSE, AM)
|
||||
addtimer(CALLBACK(src, .proc/throw_check, AM), 5)
|
||||
else
|
||||
check_break(AM)
|
||||
|
||||
|
||||
@@ -338,9 +338,9 @@
|
||||
mist_time = 70 //7 seconds on freezing temperature to disperse existing mist
|
||||
if(watertemp == "boiling")
|
||||
mist_time = 20 //2 seconds on boiling temperature to build up mist
|
||||
addtimer(src, "update_mist", mist_time)
|
||||
addtimer(CALLBACK(src, .proc/update_mist), mist_time)
|
||||
else
|
||||
addtimer(src, "update_mist", 250) //25 seconds for mist to disperse after being turned off
|
||||
addtimer(CALLBACK(src, .proc/update_mist), 250) //25 seconds for mist to disperse after being turned off
|
||||
|
||||
/obj/machinery/shower/proc/update_mist()
|
||||
if(on)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
update_self()
|
||||
forceMove(mob_info.spawn_point)
|
||||
if(!mob_info.is_trap)
|
||||
addtimer(src, "despawn", mob_info.lifetime)
|
||||
addtimer(CALLBACK(src, .proc/despawn), mob_info.lifetime)
|
||||
|
||||
/obj/effect/nanomob/proc/update_self()
|
||||
if(!mob_info)
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
var/water_timer = 0
|
||||
|
||||
/obj/effect/waterfall/New()
|
||||
water_timer = addtimer(src, "drip", water_frequency)
|
||||
water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/waterfall/Destroy()
|
||||
if(water_timer)
|
||||
@@ -25,4 +25,4 @@
|
||||
W.dir = dir
|
||||
spawn(1)
|
||||
W.loc = get_step(W, dir)
|
||||
water_timer = addtimer(src, "drip", water_frequency)
|
||||
water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE)
|
||||
@@ -238,7 +238,7 @@
|
||||
return null
|
||||
|
||||
D.occupant = occupant
|
||||
D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid)
|
||||
D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE)
|
||||
vacant_rooms -= D
|
||||
guests[occupant] = roomid
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
return
|
||||
|
||||
if(D.account.charge(100, transaction_purpose = "10 minutes", dest_name = name))
|
||||
D.roomtimer = addtimer(src, "process_room", PAY_INTERVAL, 0, roomid)
|
||||
D.roomtimer = addtimer(CALLBACK(src, .proc/process_room, roomid), PAY_INTERVAL, TIMER_STOPPABLE)
|
||||
else
|
||||
force_checkout(roomid)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
dream_images += pick_n_take(dreams)
|
||||
dreaming++
|
||||
for(var/i in 1 to dream_images.len)
|
||||
addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, dream_images[i], FALSE)
|
||||
addtimer(CALLBACK(src, .proc/experience_dream, dream_images[i], FALSE), ((i - 1) * rand(30,60)))
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
dream_images += pick_n_take(nightmares)
|
||||
nightmare++
|
||||
for(var/i in 1 to dream_images.len)
|
||||
addtimer(src, "experience_dream", ((i - 1) * rand(30,60)), FALSE, nightmares[i], TRUE)
|
||||
addtimer(CALLBACK(src, .proc/experience_dream, nightmares[i], TRUE), ((i - 1) * rand(30,60)))
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/proc/handle_dreams()
|
||||
|
||||
@@ -339,7 +339,7 @@ Gunshots/explosions/opening doors/less rare audio (done)
|
||||
s.loc = get_step(get_turf(s), get_dir(s, target))
|
||||
s.Show()
|
||||
s.Eat()
|
||||
addtimer(src, "wake_and_restore", rand(50, 100))
|
||||
addtimer(CALLBACK(src, .proc/wake_and_restore), rand(50, 100))
|
||||
qdel(s)
|
||||
|
||||
/obj/effect/hallucination/simple/singularity
|
||||
@@ -365,9 +365,9 @@ Gunshots/explosions/opening doors/less rare audio (done)
|
||||
for(var/i=0,i<hits,i++)
|
||||
target.playsound_local(null, 'sound/weapons/Laser.ogg', 25, 1)
|
||||
if(prob(75))
|
||||
addtimer(target, "playsound_local", rand(10,20), null, 'sound/weapons/sear.ogg', 25, 1)
|
||||
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/sear.ogg', 25, 1), rand(10,20))
|
||||
else
|
||||
addtimer(target, "playsound_local", rand(10,20), null, 'sound/weapons/effects/searwall.ogg', 25, 1)
|
||||
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/effects/searwall.ogg', 25, 1), rand(10,20))
|
||||
sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 8))
|
||||
target.playsound_local(null, get_sfx("bodyfall"), 25)
|
||||
if(2) //Esword fight
|
||||
@@ -381,9 +381,9 @@ Gunshots/explosions/opening doors/less rare audio (done)
|
||||
for(var/i=0,i<hits,i++)
|
||||
target.playsound_local(null, get_sfx("gunshot"), 25)
|
||||
if(prob(75))
|
||||
addtimer(target, "playsound_local", rand(10,20), null, 'sound/weapons/pierce.ogg', 25, 1)
|
||||
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, 'sound/weapons/pierce.ogg', 25, 1), rand(10,20))
|
||||
else
|
||||
addtimer(target, "playsound_local", rand(10,20), null, "ricochet", 25, 1)
|
||||
addtimer(CALLBACK(target, /mob/.proc/playsound_local, null, "ricochet", 25, 1), rand(10,20))
|
||||
sleep(rand(CLICK_CD_RANGE, CLICK_CD_RANGE + 8))
|
||||
target.playsound_local(null, get_sfx("bodyfall"), 25, 1)
|
||||
if(4) //Stunprod + cablecuff
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
C.throw_mode_on()
|
||||
icon_state = "firelemon_active"
|
||||
playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3)
|
||||
addtimer(src, "prime", rand(10, 60))
|
||||
addtimer(CALLBACK(src, .proc/prime), rand(10, 60))
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/grown/firelemon/burn()
|
||||
prime()
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
name = harvested_name
|
||||
desc = harvested_desc
|
||||
harvested = TRUE
|
||||
addtimer(src, "regrow", rand(regrowth_time_low, regrowth_time_high))
|
||||
addtimer(CALLBACK(src, .proc/regrow), rand(regrowth_time_low, regrowth_time_high))
|
||||
return 1
|
||||
|
||||
/obj/structure/flora/ash/proc/regrow()
|
||||
|
||||
@@ -793,7 +793,7 @@
|
||||
/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash)
|
||||
if(check_eye_prot() < intensity && (override_blindness_check || !(disabilities & BLIND)))
|
||||
overlay_fullscreen("flash", type)
|
||||
addtimer(src, "clear_fullscreen", 25, FALSE, "flash", 25)
|
||||
addtimer(CALLBACK(src, .proc/clear_fullscreen, "flash", 25), 25)
|
||||
return 1
|
||||
|
||||
/mob/living/proc/check_eye_prot()
|
||||
|
||||
@@ -527,7 +527,7 @@ Pass a positive integer as an argument to override a bot's default speed.
|
||||
turn_on() //Saves the AI the hassle of having to activate a bot manually.
|
||||
access_card = all_access //Give the bot all-access while under the AI's command.
|
||||
if(client)
|
||||
reset_access_timer_id = addtimer(src, "bot_reset", 600) //if the bot is player controlled, they get the extra access for a limited time
|
||||
reset_access_timer_id = addtimer(CALLBACK (src, .proc/bot_reset), 600, TIMER_OVERRIDE|TIMER_STOPPABLE) //if the bot is player controlled, they get the extra access for a limited time
|
||||
to_chat(src, "<span class='notice'><span class='big'>Priority waypoint set by [calling_ai] <b>[caller]</b>. Proceed to <b>[end_area.name]</b>.</span><br>[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.</span>")
|
||||
if(message)
|
||||
to_chat(calling_ai, "<span class='notice'>[bicon(src)] [name] called to [end_area.name]. [path.len-1] meters to destination.</span>")
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/New()
|
||||
..()
|
||||
addtimer(src, "inert_check", 2400)
|
||||
addtimer(CALLBACK(src, .proc/inert_check), 2400)
|
||||
|
||||
/obj/item/organ/internal/hivelord_core/proc/inert_check()
|
||||
if(!owner && !preserved)
|
||||
@@ -151,7 +151,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New()
|
||||
..()
|
||||
addtimer(src, "death", 100)
|
||||
addtimer(CALLBACK(src, .proc/death), 100)
|
||||
|
||||
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood
|
||||
name = "blood brood"
|
||||
|
||||
@@ -270,8 +270,8 @@ var/global/list/ts_spiderling_list = list()
|
||||
else
|
||||
ts_count_alive_station++
|
||||
// after 30 seconds, assuming nobody took control of it yet, offer it to ghosts.
|
||||
addtimer(src, "CheckFaction", 150)
|
||||
addtimer(src, "announcetoghosts", 300)
|
||||
addtimer(CALLBACK(src, .proc/CheckFaction), 100)
|
||||
addtimer(CALLBACK(src, .proc/announcetoghosts), 300)
|
||||
var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
U.add_hud_to(src)
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
for(var/turf/T in anchors)
|
||||
var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine)
|
||||
B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow)
|
||||
addtimer(src, "bear_fruit", growth_time)
|
||||
addtimer(CALLBACK(src, .proc/bear_fruit), growth_time)
|
||||
|
||||
/obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit()
|
||||
visible_message("<span class='danger'>the plant has borne fruit!</span>")
|
||||
|
||||
@@ -1004,8 +1004,7 @@
|
||||
return
|
||||
to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.")
|
||||
malf.malfhack = src
|
||||
malf.malfhacking = addtimer(malf, "malfhacked", 600, FALSE, src)
|
||||
|
||||
malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE)
|
||||
var/obj/screen/alert/hackingapc/A
|
||||
A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc)
|
||||
A.target = src
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
flick("coilhit", src)
|
||||
playsound(src.loc, 'sound/magic/LightningShock.ogg', 100, 1, extrarange = 5)
|
||||
tesla_zap(src, 5, power_produced)
|
||||
addtimer(src, "reset_shocked", 10)
|
||||
addtimer(CALLBACK(src, .proc/reset_shocked), 10)
|
||||
else
|
||||
..()
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
energy_to_raise = energy_to_raise * 1.25
|
||||
|
||||
playsound(src.loc, 'sound/magic/lightning_chargeup.ogg', 100, 1, extrarange = 30)
|
||||
addtimer(src, "new_mini_ball", 100)
|
||||
addtimer(CALLBACK(src, .proc/new_mini_ball), 100)
|
||||
|
||||
else if(energy < energy_to_lower && orbiting_balls.len)
|
||||
energy_to_raise = energy_to_raise / 1.25
|
||||
|
||||
@@ -887,7 +887,7 @@ won't update every console in existence) but it's more of a hassle to do. Also,
|
||||
//helper proc that guarantees the wait message will not freeze the UI
|
||||
/obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay)
|
||||
wait_message = message
|
||||
wait_message_timer = addtimer(src, "clear_wait_message", delay, TRUE)
|
||||
wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE)
|
||||
|
||||
// This is here to guarantee that we never lock the console, so long as the timer
|
||||
// process is running
|
||||
|
||||
@@ -17,18 +17,18 @@
|
||||
return 0
|
||||
|
||||
/obj/machinery/door/airlock/onShuttleMove()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
addtimer(src, "close", 0, TRUE, 0, 1)
|
||||
// Close any nearby airlocks as well
|
||||
for(var/obj/machinery/door/airlock/D in orange(1, src))
|
||||
addtimer(D, "close", 0, TRUE, 0, 1)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
INVOKE_ASYNC(src, .proc/close, 0, 1)
|
||||
// Close any nearby airlocks as well
|
||||
for(var/obj/machinery/door/airlock/D in orange(1, src))
|
||||
INVOKE_ASYNC(src, .proc/close, 0, 1)
|
||||
|
||||
/obj/machinery/door/airlock/onShuttleMove()
|
||||
. = ..()
|
||||
if(id_tag == "s_docking_airlock")
|
||||
addtimer(src, "lock", 0, TRUE)
|
||||
. = ..()
|
||||
if(id_tag == "s_docking_airlock")
|
||||
INVOKE_ASYNC(src, .proc/lock)
|
||||
|
||||
/mob/onShuttleMove()
|
||||
if(!move_on_shuttle)
|
||||
@@ -57,7 +57,7 @@
|
||||
/obj/machinery/door/airlock/postDock(obj/docking_port/stationary/S1)
|
||||
. = ..()
|
||||
if(!S1.lock_shuttle_doors && id_tag == "s_docking_airlock")
|
||||
addtimer(src, "unlock", 0, TRUE)
|
||||
INVOKE_ASYNC(src, .proc/unlock)
|
||||
|
||||
// Shuttle Rotation //
|
||||
/atom/proc/shuttleRotate(rotation)
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
if(H.nutrition >= NUTRITION_LEVEL_WELL_FED)
|
||||
to_chat(user, "<span class='warning'>You are already fully charged!</span>")
|
||||
else
|
||||
addtimer(src, "powerdraw_loop", 0, TRUE, A, H)
|
||||
INVOKE_ASYNC(src, .proc/powerdraw_loop, A, H)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>There is no charge to draw from that APC.</span>")
|
||||
else
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
return
|
||||
else
|
||||
bleedsuppress = TRUE
|
||||
addtimer(src, "resume_bleeding", amount)
|
||||
addtimer(CALLBACK(src, .proc/resume_bleeding), amount)
|
||||
|
||||
/mob/living/carbon/human/proc/resume_bleeding()
|
||||
bleedsuppress = FALSE
|
||||
|
||||
@@ -26,7 +26,7 @@ var/list/GPS_list = list()
|
||||
emped = 1
|
||||
overlays -= "working"
|
||||
overlays += "emp"
|
||||
addtimer(src, "reboot", 300)
|
||||
addtimer(CALLBACK(src, .proc/reboot), 300)
|
||||
|
||||
/obj/item/gps/proc/reboot()
|
||||
emped = FALSE
|
||||
|
||||
Reference in New Issue
Block a user