diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index c6ffc61495e..3f360d5bad7 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -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 diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 4b97b67e27e..0db57b27eae 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -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(!.) diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 8171dd66427..136e573a25e 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -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) \ No newline at end of file diff --git a/code/controllers/Processes/nano_mob_hunter.dm b/code/controllers/Processes/nano_mob_hunter.dm index fb57b6e0bbf..dc5dcba805b 100644 --- a/code/controllers/Processes/nano_mob_hunter.dm +++ b/code/controllers/Processes/nano_mob_hunter.dm @@ -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() diff --git a/code/controllers/Processes/timer.dm b/code/controllers/Processes/timer.dm deleted file mode 100644 index 4b42d69221d..00000000000 --- a/code/controllers/Processes/timer.dm +++ /dev/null @@ -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 diff --git a/code/controllers/Processes/weather.dm b/code/controllers/Processes/weather.dm index 1706b2232c0..919352313c6 100644 --- a/code/controllers/Processes/weather.dm +++ b/code/controllers/Processes/weather.dm @@ -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) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index b71531ee20f..b35bc300434 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -202,6 +202,9 @@ var/randomize_shift_time = FALSE var/enable_night_shifts = FALSE + // Developer + var/developer_express_start = 0 + /datum/configuration/New() for(var/T in subtypesof(/datum/game_mode)) var/datum/game_mode/M = T @@ -612,6 +615,8 @@ config.high_pop_mc_mode_amount = text2num(value) if("disable_high_pop_mc_mode_amount") config.disable_high_pop_mc_mode_amount = text2num(value) + if("developer_express_start") + config.developer_express_start = 1 else log_config("Unknown setting in configuration: '[name]'") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index ddde5460cef..3b5e4a6e707 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -54,7 +54,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/restart_clear = 0 var/static/restart_timeout = 0 var/static/restart_count = 0 - + var/static/random_seed //current tick limit, assigned before running a subsystem. @@ -71,7 +71,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(!random_seed) random_seed = rand(1, 1e9) rand_seed(random_seed) - + var/list/_subsystems = list() subsystems = _subsystems if(Master != src) @@ -195,6 +195,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new to_chat(world, "[msg]") log_world(msg) + if(config.developer_express_start & ticker.current_state == GAME_STATE_PREGAME) + ticker.current_state = GAME_STATE_SETTING_UP + if(!current_runlevel) SetRunLevel(1) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm new file mode 100644 index 00000000000..21fea2ee419 --- /dev/null +++ b/code/controllers/subsystem/timer.dm @@ -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/second_queue = list() //awe, yes, you've had first queue, but what about second queue? Contains: /datum/timedevent + 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 diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index 73104f212ff..d5426b3d673 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -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) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 3e8c11cc1e8..ae9a6df5b4f 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -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] diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm new file mode 100644 index 00000000000..2cf79ad318b --- /dev/null +++ b/code/datums/looping_sounds/looping_sound.dm @@ -0,0 +1,100 @@ +/* + output_atoms (list of atoms) The destination(s) for the sounds + + mid_sounds (list or soundfile) Since this can be either a list or a single soundfile you can have random sounds. May contain further lists but must contain a soundfile at the end. + mid_length (num) The length to wait between playing mid_sounds + + start_sound (soundfile) Played before starting the mid_sounds loop + start_length (num) How long to wait before starting the main loop after playing start_sound + + end_sound (soundfile) The sound played after the main loop has concluded + + chance (num) Chance per loop to play a mid_sound + volume (num) Sound output volume + muted (bool) Private. Used to stop the sound loop. + max_loops (num) The max amount of loops to run for. + direct (bool) If true plays directly to provided atoms instead of from them +*/ +/datum/looping_sound + var/list/atom/output_atoms + var/mid_sounds + var/mid_length + var/start_sound + var/start_length + var/end_sound + var/chance + var/volume = 100 + var/muted = TRUE + var/max_loops + var/direct + +/datum/looping_sound/New(list/_output_atoms = list(), start_immediately = FALSE, _direct = FALSE) + if(!mid_sounds) + WARNING("A looping sound datum was created without sounds to play.") + return + + output_atoms = _output_atoms + direct = _direct + + if(start_immediately) + start() + +/datum/looping_sound/Destroy() + stop() + output_atoms = null + return ..() + +/datum/looping_sound/proc/start(atom/add_thing) + if(add_thing) + output_atoms |= add_thing + if(!muted) + return + muted = FALSE + on_start() + +/datum/looping_sound/proc/stop(atom/remove_thing) + if(remove_thing) + output_atoms -= remove_thing + if(muted) + return + muted = TRUE + +/datum/looping_sound/proc/sound_loop(looped = 0) + if(muted || (max_loops && looped > max_loops)) + on_stop(looped) + return + if(!chance || prob(chance)) + play(get_sound(looped)) + addtimer(CALLBACK(src, .proc/sound_loop, ++looped), mid_length) + +/datum/looping_sound/proc/play(soundfile) + var/list/atoms_cache = output_atoms + var/sound/S = sound(soundfile) + if(direct) + S.channel = open_sound_channel() + S.volume = volume + for(var/i in 1 to atoms_cache.len) + var/atom/thing = atoms_cache[i] + if(direct) + SEND_SOUND(thing, S) + else + playsound(thing, S, volume) + +/datum/looping_sound/proc/get_sound(looped, _mid_sounds) + if(!_mid_sounds) + . = mid_sounds + else + . = _mid_sounds + while(!isfile(.) && !isnull(.)) + . = pickweight(.) + +/datum/looping_sound/proc/on_start() + var/start_wait = 0 + if(start_sound) + play(start_sound) + start_wait = start_length + addtimer(CALLBACK(src, .proc/sound_loop), start_wait) + +/datum/looping_sound/proc/on_stop(looped) + if(end_sound) + play(end_sound) \ No newline at end of file diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm new file mode 100644 index 00000000000..b1f5bdc6908 --- /dev/null +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -0,0 +1,7 @@ +/datum/looping_sound/showering + start_sound = 'sound/machines/shower/shower_start.ogg' + start_length = 2 + mid_sounds = list('sound/machines/shower/shower_mid1.ogg' = 1,'sound/machines/shower/shower_mid2.ogg' = 1,'sound/machines/shower/shower_mid3.ogg' = 1) + mid_length = 10 + end_sound = 'sound/machines/shower/shower_end.ogg' + volume = 20 \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 5fee7994a83..be35cf8cca8 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -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) diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 525880ee314..74cc6235bb6 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -145,6 +145,7 @@ belt = /obj/item/gun/projectile/automatic/pistol/deagle/camo l_ear = /obj/item/radio/headset/syndicate/alt l_pocket = /obj/item/pinpointer/advpinpointer + r_pocket = null // stop them getting a radio uplink, they get an implant instead backpack_contents = list( /obj/item/storage/box/engineer = 1, @@ -158,7 +159,6 @@ id_icon = "commander" id_access = "Syndicate Operative Leader" - uplink_uses = 500 /datum/outfit/admin/syndicate/officer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) . = ..() diff --git a/code/datums/spell.dm b/code/datums/spell.dm index e44c331e556..cc3b85bf94e 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -140,13 +140,13 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check var/mob/living/carbon/human/H = user - if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard)) + if(!istype(H.wear_suit, /obj/item/clothing/suit/wizrobe) && !istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/wizard) && !istype(H.wear_suit, /obj/item/clothing/suit/space/eva/plasmaman/wizard)) to_chat(user, "I don't feel strong enough without my robe.") return 0 if(!istype(H.shoes, /obj/item/clothing/shoes/sandal)) to_chat(user, "I don't feel strong enough without my sandals.") return 0 - if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard)) + if(!istype(H.head, /obj/item/clothing/head/wizard) && !istype(H.head, /obj/item/clothing/head/helmet/space/hardsuit/wizard) && !istype(H.wear_suit, /obj/item/clothing/head/helmet/space/eva/plasmaman/wizard)) to_chat(user, "I don't feel strong enough without my hat.") return 0 else if(!ishuman(user)) diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 43c3d3a61cd..6e3ead3849e 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -26,7 +26,7 @@ if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains to_chat(target, "You are somehow too bound to your current location to abandon it.") 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 diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm index c87b2b455e7..320cc137806 100644 --- a/code/datums/weather/weather.dm +++ b/code/datums/weather/weather.dm @@ -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) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index d57199d5f91..1e931690333 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -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)) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 3fdc402bb8a..55af5fae17b 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -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) diff --git a/code/game/gamemodes/changeling/powers/biodegrade.dm b/code/game/gamemodes/changeling/powers/biodegrade.dm index 5e4b0e7fa12..3103416b9e4 100644 --- a/code/game/gamemodes/changeling/powers/biodegrade.dm +++ b/code/game/gamemodes/changeling/powers/biodegrade.dm @@ -18,7 +18,7 @@ return FALSE user.visible_message("[user] vomits a glob of acid on \his [O]!", \ "We vomit acidic ooze onto our restraints!") - 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("[user] vomits a glob of acid across the front of \his [S]!", \ "We vomit acidic ooze onto our straight jacket!") - 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("[C]'s hinges suddenly begin to melt and run!") to_chat(user, "We vomit acidic goop onto the interior of [C]!") - 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("[src] shifts and starts to fall apart!") to_chat(user, "We secrete acidic enzymes from our skin and begin melting our cocoon...") - 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) diff --git a/code/game/gamemodes/changeling/powers/fakedeath.dm b/code/game/gamemodes/changeling/powers/fakedeath.dm index 156e8ffb896..a166fd74502 100644 --- a/code/game/gamemodes/changeling/powers/fakedeath.dm +++ b/code/game/gamemodes/changeling/powers/fakedeath.dm @@ -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 diff --git a/code/game/gamemodes/changeling/powers/fleshmend.dm b/code/game/gamemodes/changeling/powers/fleshmend.dm index 08786b6d6db..56417ac4335 100644 --- a/code/game/gamemodes/changeling/powers/fleshmend.dm +++ b/code/game/gamemodes/changeling/powers/fleshmend.dm @@ -31,7 +31,7 @@ by quick repeated use!") recent_uses++ - addtimer(src, "fleshmend", 0, FALSE, user) + INVOKE_ASYNC(src, .proc/fleshmend, user) feedback_add_details("changeling_powers","RR") return TRUE diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 57ce00c92af..8f4b5dab95e 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -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") diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index e9a830e7ed6..09d8a84539a 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -43,7 +43,8 @@ to_chat(B.host, "You feel the captive mind of [src] begin to resist your control.") 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) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 44333b774cc..3294f1f3a0b 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -141,8 +141,9 @@ wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) + if(!wizard_mob.get_species() == "Plasmaman")//handled in the species file for plasmen on the afterjob equip proc for now + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) if(wizard_mob.backbag == 2) wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(wizard_mob), slot_back) if(wizard_mob.backbag == 3) diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index baded609280..ce07e7bb920 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -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) diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index bff4bda660d..db78e8c82ef 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -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 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 61a528ed93d..59297e04b30 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -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 diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 13a2ba49ddd..d80a628927b 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -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) diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index 48637f9e67f..a4c73cc8fac 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -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 diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index 98e7b6f342d..b990029423c 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -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) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 02f89f6f0f6..f832b446cd5 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -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) diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index fa65008d307..7fb397297d7 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -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-- diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index aa3ecd8376b..5ad00157a02 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -58,4 +58,4 @@ /datum/effect_system/explosion/smoke/start() ..() - addtimer(src, "create_smoke", 5) + addtimer(CALLBACK(src, .proc/create_smoke), 5) diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index 64421b35fe9..385067e0611 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -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) diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index 9ecafee37f0..4cf5e79cc77 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -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 diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index 3f56932371d..0e2e3cc3785 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -208,7 +208,7 @@ to_chat(I.owner, "Your photon projector implant overheats and deactivates!") 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, "Your photon projector is running too hot to be used again so quickly!") 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 diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index d61aea153d7..e1ee7029439 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -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("[src] buzzes violently!") diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 3382292bc3a..b321721b639 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -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)) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index b52a92112eb..fe132c107b0 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -385,7 +385,7 @@ RCD buzz loudly!","[src] begins \ vibrating violently!") // 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) diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index 381e6177bb2..6164ed01047 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -85,7 +85,7 @@ target.overlays += image_overlay if(!nadeassembly) to_chat(user, "You plant the bomb. Timer counting down from [det_time].") - 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)](?) (FLW) suicided with [src.name] at ([user.x],[user.y],[user.z] - JMP)",0,1) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index c6899d71c22..de85ec64a58 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -422,7 +422,7 @@ message_admins("grenade primed by an assembly, attached by [key_name_admin(M)](?) (FLW) and last touched by [key_name_admin(last)](?) (FLW) ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).") 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])") diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm index d24f93736e5..d0dc2c8fe06 100644 --- a/code/game/objects/items/weapons/grenades/ghettobomb.dm +++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm @@ -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() diff --git a/code/game/objects/items/weapons/legcuffs.dm b/code/game/objects/items/weapons/legcuffs.dm index b238899676b..e6ba57ff8da 100644 --- a/code/game/objects/items/weapons/legcuffs.dm +++ b/code/game/objects/items/weapons/legcuffs.dm @@ -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)) diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 0738d57e8fb..fcd233318d5 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -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 diff --git a/code/game/objects/structures/barsign.dm b/code/game/objects/structures/barsign.dm index 6c627bb40ce..d392deeec81 100644 --- a/code/game/objects/structures/barsign.dm +++ b/code/game/objects/structures/barsign.dm @@ -105,7 +105,7 @@ to_chat(user, "Nothing interesting happens!") return to_chat(user, "You emag the barsign. Takeover in progress...") - addtimer(src, "post_emag", 100) + addtimer(CALLBACK(src, .proc/post_emag), 100) /obj/structure/sign/barsign/proc/post_emag() if(broken || emagged) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index 284001de2bf..d5dccaf0d03 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -146,6 +146,7 @@ return ..() /obj/structure/grille/proc/build_window(obj/item/stack/sheet/S, mob/user) + var/dir_to_set = NORTH if(!istype(S) || !user) return if(broken) @@ -160,59 +161,40 @@ if(!getRelativeDirection(src, user) && (user.loc != loc)) //essentially a cardinal direction adjacent or sharing same loc check to_chat(user, "You can't reach.") return - if(/obj/structure/window/full in loc) //check for a full window already present (blocks the whole tile) - to_chat(user, "There is already a full window there.") - return - var/selection = alert(user, "What type of window would you like to place?", "Window Construction", "One Direction", "Full", "Cancel") - if(selection == "Cancel") - return - if(selection == "Full") - if(S.get_amount() < 2) - to_chat(user, "You need at least two sheets of glass for that!") + if(loc == user.loc) + dir_to_set = user.dir + else + if(x == user.x) + if(y > user.y) + dir_to_set = SOUTH + else + dir_to_set = NORTH + else if(y == user.y) + if(x > user.x) + dir_to_set = WEST + else + dir_to_set = EAST + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set) + to_chat(user, "There is already a window facing this way there.") return - if(do_after(user, 20, target = src)) //glass doesn't have a toolspeed, so no multiplier - if(broken || !anchored || !src) //make sure the grille is still intact, anchored, and exists! - return - if(S.get_amount() < 2) //make sure we still have enough for this! - return - if(!getRelativeDirection(src, user) && (user.loc != loc)) //make sure we can still do this from our location - return - var/obj/structure/window/W = new S.full_window(get_turf(src)) - S.use(2) - W.anchored = 0 - W.state = 0 - to_chat(user, "You place [W] on [src].") - W.update_icon() - return - if(selection == "One Direction") - var/dir_selection = input("Which direction will this window face?", "Direction") as null|anything in list("north", "east", "south", "west") - if(!dir_selection) + to_chat(user, "You start placing the window...") + if(do_after(user, 20, target = src)) + if(!loc || !anchored) //Grille destroyed or unanchored while waiting return - var/temp_dir = text2dir(dir_selection) - for(var/obj/structure/window/W in loc) - if(istype(W, /obj/structure/window/full)) //double checking in case a full window was created while selecting direction - to_chat(user, "There is already a full window there.") + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. + to_chat(user, "There is already a window facing this way there.") return - if(W.dir == temp_dir) //to avoid building a window on top of an existing window - to_chat(user, "There is already a window facing this direction there.") - return - if(do_after(user, 20, target = src)) - if(broken || !anchored || !src) //make sure the grille is still intact, anchored, and exists! - return - if(S.get_amount() < 1) //make sure we still have enough fir this! - to_chat(user, "You need at least one sheet of glass for that!") - return - if(!getRelativeDirection(src, user) && (user.loc != loc)) //make sure we can still do this from our location - return - var/obj/structure/window/W = new S.created_window(get_turf(src)) - S.use(1) - W.setDir(temp_dir) - W.ini_dir = temp_dir - W.anchored = 0 - W.state = 0 - to_chat(user, "You place [W] on [src].") - W.update_icon() - return + var/obj/structure/window/W = new S.created_window(get_turf(src)) + S.use(1) + W.setDir(dir_to_set) + W.ini_dir = dir_to_set + W.anchored = 0 + W.state = 0 + to_chat(user, "You place the [W] on [src].") + W.update_icon() + return /obj/structure/grille/attacked_by(obj/item/I, mob/living/user) user.changeNext_move(CLICK_CD_MELEE) diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 002bbb566d6..367be27b6dc 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -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) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 7cc9c97dfdd..3ea23108662 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -249,9 +249,11 @@ var/ismist = 0 //needs a var so we can make it linger~ var/watertemp = "normal" //freezing, normal, or boiling var/mobpresent = 0 //true if there is a mob on the shower's loc, this is to ease process() + var/datum/looping_sound/showering/soundloop /obj/machinery/shower/New(turf/T, newdir = SOUTH, building = FALSE) ..() + soundloop = new(list(src), FALSE) if(building) dir = newdir pixel_x = 0 @@ -264,8 +266,8 @@ layer = FLY_LAYER /obj/machinery/shower/Destroy() - if(mymist) - QDEL_NULL(mymist) + QDEL_NULL(mymist) + QDEL_NULL(soundloop) return ..() //add heat controls? when emagged, you can freeze to death in it? @@ -282,6 +284,7 @@ on = !on update_icon() if(on) + soundloop.start() if(M.loc == loc) wash(M) check_heat(M) @@ -289,6 +292,8 @@ for(var/atom/movable/G in src.loc) G.clean_blood() G.water_act(100, convertHeat(), src) + else + soundloop.stop() /obj/machinery/shower/attackby(obj/item/I as obj, mob/user as mob, params) if(I.type == /obj/item/analyzer) @@ -338,9 +343,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) diff --git a/code/game/sound.dm b/code/game/sound.dm index 39bf16bc697..66746ece0ae 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -1,4 +1,4 @@ -/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, frequency = null, channel = 0, pressure_affected = TRUE) +/proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE) if(isarea(source)) error("[source] is an area and is trying to make the sound: [soundin]") return @@ -11,7 +11,10 @@ // Looping through the player list has the added bonus of working for mobs inside containers var/sound/S = sound(get_sfx(soundin)) var/maxdistance = (world.view + extrarange) * 3 - for(var/P in player_list) + var/list/listeners = player_list + if(!ignore_walls) //these sounds don't carry through walls + listeners = listeners & hearers(maxdistance, turf_source) + for(var/P in listeners) var/mob/M = P if(!M || !M.client) continue @@ -78,13 +81,15 @@ S.y = 1 S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) - src << S + SEND_SOUND(src, S) -/client/proc/playtitlemusic() - if(!ticker || !ticker.login_music || config.disable_lobby_music) - return - if(prefs.sound & SOUND_LOBBY) - src << sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBYMUSIC) // MAD JAMS +/proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, falloff = FALSE, channel = 0, pressure_affected = FALSE, sound/S) + if(!S) + S = sound(get_sfx(soundin)) + for(var/m in player_list) + if(ismob(m) && !isnewplayer(m)) + var/mob/M = m + M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S) /proc/open_sound_channel() var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used @@ -93,7 +98,13 @@ next_channel = 1 /mob/proc/stop_sound_channel(chan) - src << sound(null, repeat = 0, wait = 0, channel = chan) + SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan)) + +/client/proc/playtitlemusic() + if(!ticker || !ticker.login_music || config.disable_lobby_music) + return + if(prefs.sound & SOUND_LOBBY) + SEND_SOUND(src, sound(ticker.login_music, repeat = 0, wait = 0, volume = 85, channel = CHANNEL_LOBBYMUSIC)) // MAD JAMS /proc/get_rand_frequency() return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. diff --git a/code/modules/arcade/mob_hunt/mob_avatar.dm b/code/modules/arcade/mob_hunt/mob_avatar.dm index 42342e0693b..e4ea98a44a6 100644 --- a/code/modules/arcade/mob_hunt/mob_avatar.dm +++ b/code/modules/arcade/mob_hunt/mob_avatar.dm @@ -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) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index f285d4434da..4d2384ceb67 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -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) \ No newline at end of file + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) \ No newline at end of file diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm index d9299dfe061..e86123abc2e 100644 --- a/code/modules/awaymissions/mission_code/spacehotel.dm +++ b/code/modules/awaymissions/mission_code/spacehotel.dm @@ -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) diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index d81f1f7e02c..7d6275e09e4 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -152,3 +152,7 @@ /datum/gear/hat/flowerpin display_name = "hair flower" path = /obj/item/clothing/head/hairflower + +/datum/gear/hat/kitty + display_name = "kitty headband" + path = /obj/item/clothing/head/kitty diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 22af9cb4590..23f99ec24c2 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -398,3 +398,13 @@ icon_state = "plasmaman_Nukeops_helmet0" base_state = "plasmaman_Nukeops_helmet" armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50) + +//WIZARD +/obj/item/clothing/suit/space/eva/plasmaman/wizard + name = "robed plasmaman suit" + icon_state = "plasmamanWizardBlue_suit" + +/obj/item/clothing/head/helmet/space/eva/plasmaman/wizard + name = "wizard hat" + icon_state = "plasmamanWizardBlue_helmet0" + base_state = "plasmamanWizardBlue_helmet" diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index f7135acd270..a075dbdfba3 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -696,6 +696,12 @@ user.update_inv_head() return 1 +/obj/item/clothing/head/beret/fluff/elo //V-Force_Bomber: E.L.O. + name = "E.L.O.'s medical beret" + desc = "E.L.O.s personal medical beret, issued by Nanotrassen and awarded along with her medal." + icon = 'icons/obj/custom_items.dmi' + icon_state = "elo-beret" + //////////// Suits //////////// /obj/item/clothing/suit/fluff icon = 'icons/obj/custom_items.dmi' diff --git a/code/modules/flufftext/Dreaming.dm b/code/modules/flufftext/Dreaming.dm index d01b5595d92..6a1d28eaf87 100644 --- a/code/modules/flufftext/Dreaming.dm +++ b/code/modules/flufftext/Dreaming.dm @@ -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() diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 621b8ec37ea..021db59f8a4 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -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,iERROR: APC access disabled, hack attempt canceled.") - malfhacking = 0 - malfhack = null + deltimer(malfhacking) + // This proc handles cleanup of screen notifications and + // messenging the client + malfhacked(malfhack) if(aiRestorePowerRoutine) adjustOxyLoss(1) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index c040fbe1ed2..986795ccd1e 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -193,6 +193,9 @@ bots_list -= src QDEL_NULL(Radio) QDEL_NULL(access_card) + if(reset_access_timer_id) + deltimer(reset_access_timer_id) + reset_access_timer_id = null if(radio_controller && bot_filter) radio_controller.remove_object(bot_core, control_freq) QDEL_NULL(bot_core) @@ -527,7 +530,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, "Priority waypoint set by [calling_ai] [caller]. Proceed to [end_area.name].
[path.len-1] meters to destination. You have been granted additional door access for 60 seconds.
") if(message) to_chat(calling_ai, "[bicon(src)] [name] called to [end_area.name]. [path.len-1] meters to destination.") diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index e38ed7624ba..a026d33ed8d 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -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" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 2a1e0f17ef4..cd01006546c 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -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), 150) + addtimer(CALLBACK(src, .proc/announcetoghosts), 300) var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm index 9e6396dbde0..57412eb5a0d 100644 --- a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -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("the plant has borne fruit!") diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 23c6e2a1f15..9c3d64ef4fc 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -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 diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 8bca07ccd45..aa25105cba6 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -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 ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index f2ffbb9b857..9cfd526585e 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -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 diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm new file mode 100644 index 00000000000..f6881208a32 --- /dev/null +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -0,0 +1,130 @@ +/obj/item/gun/blastcannon + name = "pipe gun" + desc = "A pipe welded onto a gun stock, with a mechanical trigger. The pipe has an opening near the top, and there seems to be a spring loaded wheel in the hole." + icon_state = "empty_blastcannon" + var/icon_state_loaded = "loaded_blastcannon" + item_state = "blastcannon_empty" + w_class = WEIGHT_CLASS_NORMAL + force = 10 + fire_sound = 'sound/weapons/blastcannon.ogg' + needs_permit = FALSE + clumsy_check = FALSE + randomspread = FALSE + + var/obj/item/transfer_valve/bomb + +/obj/item/gun/blastcannon/Destroy() + QDEL_NULL(bomb) + return ..() + +/obj/item/gun/blastcannon/attack_self(mob/user) + if(bomb) + bomb.forceMove(user.loc) + user.put_in_hands(bomb) + user.visible_message("[user] detaches [bomb] from [src].") + bomb = null + update_icon() + return ..() + +/obj/item/gun/blastcannon/update_icon() + if(bomb) + icon_state = icon_state_loaded + name = "blast cannon" + desc = "A makeshift device used to concentrate a bomb's blast energy to a narrow wave." + else + icon_state = initial(icon_state) + name = initial(name) + desc = initial(desc) + +/obj/item/gun/blastcannon/attackby(obj/O, mob/user) + if(istype(O, /obj/item/transfer_valve)) + var/obj/item/transfer_valve/T = O + if(!T.tank_one || !T.tank_two) + to_chat(user, "What good would an incomplete bomb do?") + return FALSE + if(!user.drop_item()) + to_chat(user, "[T] seems to be stuck to your hand!") + return FALSE + user.visible_message("[user] attaches [T] to [src]!") + T.forceMove(src) + bomb = T + update_icon() + return TRUE + return ..() + +/obj/item/gun/blastcannon/proc/calculate_bomb() + if(!istype(bomb)||!istype(bomb.tank_one)||!istype(bomb.tank_two)) + return 0 + var/datum/gas_mixture/temp = new() //directional buff. + temp.volume = 60 + temp.merge(bomb.tank_one.air_contents.remove_ratio(1)) + temp.merge(bomb.tank_two.air_contents.remove_ratio(2)) + for(var/i in 1 to 6) + temp.react() + var/pressure = temp.return_pressure() + qdel(temp) + if(pressure < TANK_FRAGMENT_PRESSURE) + return 0 + return (pressure / TANK_FRAGMENT_SCALE) + +/obj/item/gun/blastcannon/afterattack(atom/target, mob/user, flag, params) + if((!bomb) || (!target) || (get_dist(get_turf(target), get_turf(user)) <= 2)) + return ..() + var/power = calculate_bomb() + QDEL_NULL(bomb) + update_icon() + var/heavy = power * 0.2 + var/medium = power * 0.5 + var/light = power + user.visible_message("[user] opens [bomb] on \his [name] and fires a blast wave at [target]!","You open [bomb] on your [name] and fire a blast wave at [target]!") + playsound(user, "explosion", 100, 1) + var/turf/starting = get_turf(user) + var/turf/targturf = get_turf(target) + message_admins("Blast wave fired from [ADMIN_COORDJMP(starting)] ([get_area_name(user, TRUE)]) at [ADMIN_COORDJMP(targturf)] ([target.name]) by [key_name_admin(user)] with power [heavy]/[medium]/[light].") + log_game("Blast wave fired from ([starting.x], [starting.y], [starting.z]) ([get_area_name(user, TRUE)]) at ([target.x], [target.y], [target.z]) ([target]) by [key_name(user)] with power [heavy]/[medium]/[light].") + var/obj/item/projectile/blastwave/BW = new(loc, heavy, medium, light) + BW.preparePixelProjectile(target, get_turf(target), user, params, 0) + BW.fire() + +/obj/item/projectile/blastwave + name = "blast wave" + icon_state = "blastwave" + damage = 0 + nodamage = FALSE + forcedodge = TRUE + range = 150 + var/heavyr = 0 + var/mediumr = 0 + var/lightr = 0 + +/obj/item/projectile/blastwave/New(loc, _h, _m, _l) + ..() + heavyr = _h + mediumr = _m + lightr = _l + +/obj/item/projectile/blastwave/Range() + ..() + var/amount_destruction = 0 + if(heavyr) + amount_destruction = EXPLODE_DEVASTATE + else if(mediumr) + amount_destruction = EXPLODE_HEAVY + else if(lightr) + amount_destruction = EXPLODE_LIGHT + if(amount_destruction && isturf(loc)) + var/turf/T = loc + for(var/thing in T.contents) + var/atom/AM = thing + if(AM && AM.simulated) + AM.ex_act(amount_destruction) + CHECK_TICK + T.ex_act(amount_destruction) + else + qdel(src) + heavyr = max(heavyr - 1, 0) + mediumr = max(mediumr - 1, 0) + lightr = max(lightr - 1, 0) + +/obj/item/projectile/blastwave/ex_act() + return \ No newline at end of file diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index 22fd75b5e21..39c2e8c78fb 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -101,7 +101,7 @@ min_temp = 374 result_amount = 1 -/datum/chemical_reaction/plastication/on_reaction(datum/reagents/holder) +/datum/chemical_reaction/plastic_polymers/on_reaction(datum/reagents/holder, created_volume) var/obj/item/stack/sheet/plastic/P = new /obj/item/stack/sheet/plastic P.amount = 10 P.forceMove(get_turf(holder.my_atom)) diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 05038017aec..50c8e1a4cf8 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -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 diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 342931a065c..88551f49096 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -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(D, .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) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 1290212a249..d98e40700df 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -290,7 +290,7 @@ if(H.nutrition >= NUTRITION_LEVEL_WELL_FED) to_chat(user, "You are already fully charged!") else - addtimer(src, "powerdraw_loop", 0, TRUE, A, H) + INVOKE_ASYNC(src, .proc/powerdraw_loop, A, H) else to_chat(user, "There is no charge to draw from that APC.") else diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 88e8341d37c..9f732fdef4d 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -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 diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index c8ed52ec787..467f92d095e 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -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 diff --git a/config/example/config.txt b/config/example/config.txt index d0eb704ea07..46d03c48aaa 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -368,3 +368,6 @@ HIGH_POP_MC_MODE_AMOUNT 65 ##Disengage high pop mode if player count drops below this DISABLE_HIGH_POP_MC_MODE_AMOUNT 60 + +##Uncomment to enable developer start. Auto starts the server after initialization +##DEVELOPER_EXPRESS_START \ No newline at end of file diff --git a/html/changelog.html b/html/changelog.html index 054cee07cbc..9d4218f4286 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -55,7637 +55,3 @@ -->
- -

26 April 2018

-

Fox McCloud updated:

- -

Tayyyyyyy updated:

- -

uraniummeltdown updated:

- - -

25 April 2018

-

Fox McCloud updated:

- -

Tayyyyyyy updated:

- -

Xhuis updated:

- - -

24 April 2018

-

Kluys updated:

- - -

17 April 2018

-

Citinited updated:

- -

MINIMAN10000 updated:

- - -

14 April 2018

-

Anticept updated:

- -

MarsM0nd updated:

- - -

11 April 2018

-

KasparoVy updated:

- - -

08 April 2018

-

Kyep updated:

- - -

07 April 2018

-

Alffd updated:

- -

Birdtalon updated:

- -

Citinited updated:

- -

Crazylemon64 updated:

- -

Dyhr updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- -

Spacemanspark updated:

- -

uraniummeltdown updated:

- - -

06 April 2018

-

Fox McCloud updated:

- - -

05 April 2018

-

uraniummeltdown updated:

- - -

03 April 2018

-

& Deathride58 & Tigercat2000 updated:

- - -

02 April 2018

-

Desolate updated:

- -

EldritchSigma updated:

- -

Fethas updated:

- -

IK3I updated:

- -

Kyep updated:

- -

Shazbot updated:

- -

Tayyyyyyy updated:

- - -

01 April 2018

-

Tayyyyyyy, LP Spartan updated:

- - -

30 March 2018

-

matt81093 updated:

- - -

28 March 2018

-

Birdtalon updated:

- -

Fox McCloud updated:

- -

MarsM0nd updated:

- - -

25 March 2018

-

Alffd updated:

- - -

24 March 2018

-

matt81093 updated:

- -

uraniummeltdown updated:

- - -

22 March 2018

-

uraniummeltdown updated:

- - -

21 March 2018

-

MarsM0nd updated:

- - -

20 March 2018

-

MarsM0nd updated:

- - -

19 March 2018

-

Birdtalon updated:

- -

Birdtalon, LPSpartan, Shazbot updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

IK3I updated:

- -

Shazbot updated:

- - -

16 March 2018

-

MarsM0nd updated:

- - -

13 March 2018

-

Citinited updated:

- -

Piccione updated:

- - -

12 March 2018

-

Fethas updated:

- - -

08 March 2018

-

shazbot updated:

- - -

06 March 2018

-

Regen updated:

- - -

04 March 2018

-

Funce updated:

- - -

03 March 2018

-

Birdtalon updated:

- -

Citinited updated:

- -

DarkPyrolord updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

KasparoVy updated:

- -

Serket updated:

- -

Tayyyyyyy updated:

- - -

26 February 2018

-

Birdtalon updated:

- - -

21 February 2018

-

uraniummeltdown updated:

- - -

20 February 2018

-

Alffd updated:

- -

Anasari updated:

- -

Birdtalon updated:

- -

Citinited updated:

- -

Fox McCloud updated:

- -

HugoLuman updated:

- -

MarcellusPye updated:

- -

Shazbot updated:

- -

Tayyyyyyy, bryanayalalugo updated:

- - -

05 February 2018

-

IK3I updated:

- -

MarsM0nd updated:

- -

Tayyyyyyy updated:

- -

uraniummeltdown updated:

- - -

02 February 2018

-

Fethas updated:

- - -

01 February 2018

-

Squirgenheimer updated:

- -

Tayyyyyyy updated:

- -

uraniummeltdown updated:

- - -

29 January 2018

-

Fox McCloud updated:

- -

MarcellusPye updated:

- - -

27 January 2018

-

IK3I updated:

- -

uraniummeltdown updated:

- - -

26 January 2018

-

tigercat2000 updated:

- -

uraniummeltdown updated:

- - -

24 January 2018

-

IK3I updated:

- -

Kyep updated:

- -

Tayyyyyyy updated:

- - -

21 January 2018

-

KasparoVy updated:

- -

Tayyyyyyy updated:

- - -

20 January 2018

-

uraniummeltdown updated:

- - -

19 January 2018

-

Citinited updated:

- -

uraniummeltdown updated:

- - -

13 January 2018

-

Citinited updated:

- -

Kyep updated:

- - -

11 January 2018

-

Bxil updated:

- -

Citinited updated:

- -

Jountax updated:

- -

Kyep updated:

- -

Tayyyyyyy, FPK updated:

- -

uraniummeltdown updated:

- - -

07 January 2018

-

Kyep updated:

- - -

05 January 2018

-

Kyep updated:

- -

uraniummeltdown updated:

- - -

04 January 2018

-

Anasari updated:

- - -

31 December 2017

-

Alffd updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Tayyyyyyy updated:

- -

Vivalas updated:

- - -

27 December 2017

-

Tayyyyyyy updated:

- - -

23 December 2017

-

Fethas updated:

- -

Fox McCloud updated:

- -

Purpose2 updated:

- -

Santa's Lawyer updated:

- - -

21 December 2017

-

FalseIncarnate updated:

- -

Purpose2 updated:

- - -

19 December 2017

-

Santa Claus updated:

- - -

15 December 2017

-

uraniummeltdown updated:

- - -

05 December 2017

-

Kyep updated:

- - -

04 December 2017

-

Tayyyyyyy updated:

- -

Terillia updated:

- - -

02 December 2017

-

uraniummeltdown updated:

- - -

01 December 2017

-

Fox McCloud updated:

- - -

30 November 2017

-

ExitGame updated:

- -

Fox McCloud updated:

- - -

28 November 2017

-

Kyep updated:

- - -

27 November 2017

-

uraniummeltdown updated:

- - -

26 November 2017

-

FalseIncarnate updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Jountax updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- -

Ty-Omaha updated:

- -

uraniummeltdown updated:

- - -

22 November 2017

-

Fox McCloud updated:

- - -

19 November 2017

-

Fox McCloud updated:

- - -

18 November 2017

-

MarsM0nd updated:

- - -

13 November 2017

-

Alffd updated:

- -

VexingRaven updated:

- - -

12 November 2017

-

Kyep updated:

- - -

11 November 2017

-

FalseIncarnate updated:

- - -

10 November 2017

-

Allfd updated:

- -

Anasari updated:

- - -

05 November 2017

-

Fethas updated:

- -

KasparoVy updated:

- - -

04 November 2017

-

FalseIncarnate updated:

- -

FreeStylaLT updated:

- - -

01 November 2017

-

FreeStylaLT updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- - -

30 October 2017

-

NewSta updated:

- - -

29 October 2017

-

Alffd updated:

- - -

28 October 2017

-

Anasari updated:

- -

Kyep updated:

- -

Landerlow updated:

- -

Squirgenheimer updated:

- - -

27 October 2017

-

Birdtalon updated:

- -

FalseIncarnate updated:

- - -

26 October 2017

-

Anasari updated:

- -

FreeStylaLT updated:

- -

Kyep updated:

- -

TDSSS updated:

- - -

25 October 2017

-

Anasari updated:

- -

Kyep updated:

- - -

22 October 2017

-

Crazylemon64 updated:

- -

Fethas updated:

- -

McCloud and Alffd updated:

- - -

15 October 2017

-

Citinited updated:

- -

Fethas updated:

- -

Imsxz updated:

- -

Kyep updated:

- -

TullyBurnalot updated:

- -

Vivalas updated:

- - -

14 October 2017

-

Anasari updated:

- -

Fethas updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- - -

11 October 2017

-

Anasari updated:

- -

Birdtalon updated:

- -

Kyep updated:

- -

TullyBurnalot updated:

- -

scrubmcnoob updated:

- -

uraniummeltdown updated:

- - -

10 October 2017

-

Birdtalon updated:

- - -

07 October 2017

-

Birdtalon updated:

- -

imsxz updated:

- -

scrubmcnoob updated:

- -

uraniummeltdown updated:

- - -

06 October 2017

-

Birdtalon updated:

- - -

05 October 2017

-

Birdtalon updated:

- - -

02 October 2017

-

Imsxz updated:

- - -

30 September 2017

-

Jovaniph updated:

- -

Landerlow updated:

- - -

29 September 2017

-

Citinited updated:

- -

imsxz updated:

- - -

28 September 2017

-

MarsM0nd updated:

- -

uraniummeltdown updated:

- - -

05 September 2017

-

Kyep updated:

- - -

04 September 2017

-

Kyep updated:

- - -

03 September 2017

-

Birdtalon updated:

- -

Citinited updated:

- - -

02 September 2017

-

Landerlow updated:

- - -

01 September 2017

-

Birdtalon updated:

- - -

28 August 2017

-

Birdtalon updated:

- -

Birdtalon & Hylocereus updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Fox Mccloud updated:

- -

Jovaniph updated:

- -

Landerlow updated:

- -

matt81093 updated:

- - -

24 August 2017

-

Birdtalon updated:

- -

Fox McCloud updated:

- - -

16 August 2017

-

Citinited updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- - -

15 August 2017

-

Birdtalon updated:

- - -

14 August 2017

-

AndrewMontagne updated:

- -

Birdtalon updated:

- -

Citinited updated:

- -

Fethas updated:

- -

taukausanake updated:

- - -

10 August 2017

-

KasparoVy updated:

- - -

09 August 2017

-

Purpose2 updated:

- - -

06 August 2017

-

Birdtalon updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- -

Purpose2 updated:

- - -

05 August 2017

-

Purpose2 updated:

- - -

03 August 2017

-

Fox McCloud updated:

- - -

02 August 2017

-

Fox McCloud updated:

- - -

31 July 2017

-

Birdtalon updated:

- - -

30 July 2017

-

FreeStylaLT updated:

- -

Kyep updated:

- - -

29 July 2017

-

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Purpose2 updated:

- - -

27 July 2017

-

Birdtalon updated:

- - -

26 July 2017

-

Citinited updated:

- - -

25 July 2017

-

Birdtalon updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Kluys, Kyep & DarkLordpyro updated:

- -

Kyep updated:

- -

imsxz updated:

- -

owenowen212 updated:

- - -

24 July 2017

-

FlattestGuitar updated:

- -

Kyep updated:

- - -

23 July 2017

-

Fox McCloud updated:

- - -

22 July 2017

-

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- - -

21 July 2017

-

Purpose2 updated:

- - -

20 July 2017

-

Purpose2 updated:

- - -

19 July 2017

-

Fox McCloud updated:

- - -

18 July 2017

-

Fox McCloud updated:

- -

Purpose2 updated:

- - -

16 July 2017

-

Ataman updated:

- -

Crazylemon64 updated:

- -

KasparoVy updated:

- - -

15 July 2017

-

Fox McCloud updated:

- -

Kyep updated:

- -

imsxz updated:

- - -

13 July 2017

-

Fox McCloud updated:

- -

Shazbot-coding, Driker-Sprites updated:

- - -

12 July 2017

-

Fox McCloud updated:

- - -

11 July 2017

-

Fethas and tigercat updated:

- - -

10 July 2017

-

AffectedArc07 updated:

- -

Birdtalon updated:

- -

Citinited updated:

- -

Fox McCloud updated:

- - -

08 July 2017

-

Citinited updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- - -

05 July 2017

-

Purpose2 updated:

- - -

04 July 2017

-

Birdtalon updated:

- -

Citinited updated:

- -

Citinited & LightFire53 updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Ionward updated:

- -

Kyep updated:

- -

Vivalas updated:

- - -

03 July 2017

-

Fethas updated:

- -

fludd12 updated:

- - -

01 July 2017

-

Fox McCloud updated:

- - -

30 June 2017

-

fludd12 updated:

- - -

29 June 2017

-

Fox McCloud updated:

- -

Kluys updated:

- - -

28 June 2017

-

Fox McCloud updated:

- -

Purpose2 and Re-Opened by Fethas updated:

- - -

27 June 2017

-

Alexshreds updated:

- -

Citinited updated:

- -

Code Fethas and Sprites Phantasmicdream updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- -

ProperPants updated:

- -

Purpose2 updated:

- -

tigercat2000 updated:

- - -

26 June 2017

-

Crazylemon64 updated:

- -

Kyep updated:

- -

Purpose2 updated:

- - -

25 June 2017

-

Kyep updated:

- - -

24 June 2017

-

Allfd updated:

- -

Kyep updated:

- -

Purpose2 updated:

- -

SamHPurp updated:

- - -

23 June 2017

-

Kyep updated:

- -

Vivalas updated:

- - -

22 June 2017

-

Crazylemon64 updated:

- -

Purpose2 updated:

- - -

21 June 2017

-

tigercat2000 updated:

- - -

20 June 2017

-

Purpose2 updated:

- - -

19 June 2017

-

Fox McCloud updated:

- - -

18 June 2017

-

Citinited updated:

- - -

16 June 2017

-

Alffd updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Kyep updated:

- -

Purpose2 updated:

- -

TullyBurnalot updated:

- -

alexkar598 updated:

- - -

13 June 2017

-

KasparoVy updated:

- -

Vivalas updated:

- - -

10 June 2017

-

Alffd updated:

- -

Citinited updated:

- -

FalseIncarnate updated:

- - -

09 June 2017

-

Alexshreds updated:

- -

FlattyPatty updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

alexkar598 updated:

- -

tigercat2000 updated:

- - -

08 June 2017

-

Alexshreds updated:

- - -

07 June 2017

-

Fethas updated:

- -

tigercat2000 updated:

- - -

06 June 2017

-

DarkPyrolord updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- -

imsxz updated:

- - -

04 June 2017

-

Crazylemon64 updated:

- -

FreeStylaLT updated:

- - -

03 June 2017

-

FlattestGuitar updated:

- - -

01 June 2017

-

Alexshreds updated:

- -

FreeStylaLT updated:

- -

Kyep updated:

- -

Purpose2 updated:

- -

tigercat2000 updated:

- - -

31 May 2017

-

FalseIncarnate updated:

- -

Xantholne updated:

- - -

30 May 2017

-

Fruerlund updated:

- - -

29 May 2017

-

Jovaniph updated:

- -

Purpose2 updated:

- -

Tayyyyyyy, FlattestGuitar updated:

- - -

28 May 2017

-

Fox McCloud updated:

- - -

27 May 2017

-

Crazylemon64 updated:

- - -

26 May 2017

-

FlattyPatty updated:

- - -

25 May 2017

-

FalseIncarnate updated:

- -

Fethas updated:

- -

Kyep updated:

- -

Purpose2 updated:

- -

Tayyyyyyy, PhantasmicDream updated:

- - -

24 May 2017

-

Kyep updated:

- - -

23 May 2017

-

Fethas updated:

- -

Purpose2 updated:

- - -

22 May 2017

-

DarkPyrolord updated:

- -

Kyep updated:

- - -

21 May 2017

-

KasparoVy updated:

- - -

20 May 2017

-

FlattestGuitar updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Kyep: updated:

- -

Xantholne updated:

- - -

19 May 2017

-

IK3I updated:

- -

MarsM0nd updated:

- -

Purpose2 updated:

- - -

18 May 2017

-

FreeStylaLT updated:

- -

Purpose2 updated:

- -

TullyBurnalot updated:

- - -

17 May 2017

-

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

Purpose2 updated:

- -

Travelling Merchant updated:

- - -

16 May 2017

-

FlattestGuitar updated:

- -

FreeStylaLT updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

15 May 2017

-

TullyBurnalot updated:

- - -

13 May 2017

-

Fethas updated:

- -

KasparoVy updated:

- - -

12 May 2017

-

Fethas updated:

- -

Xantholne updated:

- - -

11 May 2017

-

Fethas updated:

- -

Fox McCloud updated:

- - -

09 May 2017

-

Fethas updated:

- - -

08 May 2017

-

Purpose2 updated:

- -

uraniummeltdown updated:

- - -

07 May 2017

-

GunDOSMk1 updated:

- -

Purpose2 updated:

- -

Twinmold updated:

- - -

06 May 2017

-

Allfd updated:

- -

Fethas updated:

- -

LightFire69 updated:

- -

uraniummeltdown updated:

- - -

05 May 2017

-

Fethas updated:

- - -

01 May 2017

-

Fethas updated:

- - -

30 April 2017

-

scrubmcnoob updated:

- - -

29 April 2017

-

FalseIncarnate updated:

- -

Phantasmic Dream Art, Fethas PR updated:

- -

uraniummeltdown updated:

- - -

28 April 2017

-

Alffd updated:

- - -

27 April 2017

-

Kyep updated:

- - -

26 April 2017

-

FalseIncarnate updated:

- -

pinatacolada updated:

- - -

25 April 2017

-

Flattest updated:

- -

uraniummeltdown updated:

- - -

24 April 2017

-

Flattest updated:

- - -

23 April 2017

-

Citinited updated:

- -

Crazylemon64 updated:

- -

IK3I updated:

- -

Tayyyyyyy updated:

- - -

21 April 2017

-

Anticept updated:

- -

Fox McCloud updated:

- -

Lady-Luck updated:

- -

Spacemanspark updated:

- -

Twinmold93 updated:

- -

alexkar598 updated:

- -

monster860 updated:

- - -

19 April 2017

-

IK3I updated:

- -

Xantholne updated:

- - -

17 April 2017

-

KasparoVy updated:

- -

Krausus updated:

- - -

16 April 2017

-

FalseIncarnate updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Twinmold93 updated:

- -

Xantholne updated:

- -

ZomgPonies updated:

- -

uraniummeltdown updated:

- - -

11 April 2017

-

FalseIncarnate updated:

- - -

09 April 2017

-

Fox McCloud updated:

- -

Purpose2 updated:

- - -

06 April 2017

-

Fox McCloud updated:

- -

davipatury updated:

- - -

05 April 2017

-

KasparoVy updated:

- -

Purpose2 updated:

- -

Twinmold93 updated:

- - -

04 April 2017

-

FalseIncarnate updated:

- - -

03 April 2017

-

Kyep updated:

- - -

02 April 2017

-

KasparoVy updated:

- -

Kyep updated:

- - -

31 March 2017

-

Krausus updated:

- -

Kyep updated:

- - -

30 March 2017

-

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

29 March 2017

-

Purpose2 updated:

- - -

28 March 2017

-

KasparoVy updated:

- -

uraniummeltdown updated:

- - -

27 March 2017

-

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Purpose2 updated:

- - -

26 March 2017

-

FalseIncarnate updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Purpose2 updated:

- -

Ty-Omaha updated:

- -

uraniummeltdown updated:

- - -

25 March 2017

-

Crazylemon64 updated:

- -

Kyep updated:

- - -

24 March 2017

-

KasparoVy updated:

- -

uraniummeltdown updated:

- - -

23 March 2017

-

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- -

Norgad updated:

- -

uraniummeltdown updated:

- - -

22 March 2017

-

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

KasparoVy updated:

- -

Krausus updated:

- - -

21 March 2017

-

Crazylemon64 updated:

- -

KasparoVy updated:

- -

uraniummeltdown updated:

- - -

20 March 2017

-

Jovaniph updated:

- -

KasparoVy updated:

- -

Purpose2 updated:

- -

Tayyyyyyy updated:

- - -

19 March 2017

-

Jovaniph updated:

- -

Krausus updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

18 March 2017

-

Fox McCloud updated:

- -

Jovaniph updated:

- -

uraniummeltdown updated:

- - -

16 March 2017

-

FlattestGeetar updated:

- -

Jovaniph updated:

- -

Markolie updated:

- - -

15 March 2017

-

uraniummeltdown updated:

- - -

14 March 2017

-

uraniummeltdown updated:

- - -

13 March 2017

-

Kyep updated:

- -

uraniummeltdown updated:

- - -

12 March 2017

-

Crazylemon64 updated:

- -

Jovaniph updated:

- -

Kyep updated:

- -

Markolie updated:

- -

Twinmold93 updated:

- - -

11 March 2017

-

Developed by XDTM, ported by davipatury updated:

- -

Flatty Patty updated:

- -

FlattyPatty updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Markolie updated:

- -

davipatury updated:

- -

uraniummeltdown updated:

- - -

10 March 2017

-

Jovaniph updated:

- -

Markolie updated:

- - -

09 March 2017

-

Crazylemon64 updated:

- -

LordJike updated:

- -

Markolie updated:

- - -

08 March 2017

-

Alffd updated:

- -

FlaaaaaattestGuitar updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

07 March 2017

-

Crazylemon64 updated:

- -

Kyep updated:

- -

MarsM0nd updated:

- - -

05 March 2017

-

Kyep updated:

- -

MarsM0nd updated:

- - -

04 March 2017

-

Markolie updated:

- -

davipatury updated:

- - -

03 March 2017

-

FlauntestGuitar updated:

- -

uraniummeltdown updated:

- - -

01 March 2017

-

Crazylemon64 updated:

- -

Fethas updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Purpose2 updated:

- - -

28 February 2017

-

KasparoVy updated:

- - -

27 February 2017

-

uraniummeltdown updated:

- - -

26 February 2017

-

Crazylemon64 updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

davipatury updated:

- - -

25 February 2017

-

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

KasparoVy updated:

- -

davipatury updated:

- - -

24 February 2017

-

FalseIncarnate updated:

- -

Kyep updated:

- -

Markolie updated:

- -

Purpose2 updated:

- -

Twinmold93 updated:

- -

davipatury updated:

- - -

23 February 2017

-

Krausus updated:

- -

Markolie updated:

- - -

22 February 2017

-

KasparoVy updated:

- - -

21 February 2017

-

Crazylemon64 updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

Markolie updated:

- -

davipatury updated:

- -

uraniummeltdown updated:

- - -

20 February 2017

-

Markolie updated:

- -

davipatury updated:

- -

tigercat2000 updated:

- - -

19 February 2017

-

Markolie updated:

- - -

18 February 2017

-

Alffd updated:

- -

Crazylemon64 updated:

- -

Krausus updated:

- -

Kyep updated:

- -

Markolie updated:

- -

uraniummeltdown updated:

- - -

17 February 2017

-

Crazylemon64 updated:

- -

Ported by Markolie, developed by AnturK updated:

- - -

16 February 2017

-

Crazylemon64 updated:

- -

KasparoVy updated:

- -

davipatury updated:

- -

uraniummeltdown updated:

- - -

15 February 2017

-

Alexshreds updated:

- -

Ausops updated:

- -

Crazylemon64 updated:

- -

FlimFlamm updated:

- -

KasparoVy updated:

- -

Markolie updated:

- -

Twinmold93 updated:

- -

uraniummeltdown updated:

- - -

14 February 2017

-

Developed by Cyberboss, ported by Markolie updated:

- -

DrunkDwarf updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Markolie updated:

- - -

13 February 2017

-

Crazylemon64 updated:

- -

Fethas updated:

- -

Fluff12 updated:

- -

Fox McCloud updated:

- -

Kyep updated:

- -

Markolie updated:

- -

uraniummeltdown updated:

- - -

12 February 2017

-

Fox McCloud updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

11 February 2017

-

Markolie updated:

- - -

10 February 2017

-

Fox McCloud updated:

- -

KasparoVy, Krausus updated:

- -

Markolie updated:

- -

tigercat2000 updated:

- -

uraniummeltdown updated:

- - -

08 February 2017

-

Fox McCloud updated:

- - -

07 February 2017

-

Fox MCCloud updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

06 February 2017

-

Fox McCloud updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

05 February 2017

-

Fethas updated:

- -

Fox McCloud and Core0verload updated:

- -

Ported by Markolie, developed by KorPhaeron and others from /tg/. The Hierophant was developed by ChangelingRain. updated:

- -

uraniummeltdown updated:

- - -

03 February 2017

-

DrunkDwarf updated:

- - -

02 February 2017

-

Alexshreds updated:

- -

Kyep updated:

- -

uraniummeltdown updated:

- - -

31 January 2017

-

Ar3nn updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

uraniummeltdown updated:

- - -

30 January 2017

-

Alexshreds updated:

- - -

29 January 2017

-

uraniummeltdown updated:

- - -

27 January 2017

-

Alffd updated:

- -

Fox McCloud updated:

- -

uraniummeltdown updated:

- - -

26 January 2017

-

FalseIncarnate updated:

- -

Fethas updated:

- -

FreeStylaLT updated:

- - -

25 January 2017

-

FlattestGuitar updated:

- -

scrubmcnoob updated:

- - -

23 January 2017

-

FalseIncarnate updated:

- -

Stratus updated:

- - -

18 January 2017

-

AndriiYukhymchak updated:

- -

Fethas updated:

- -

KasparoVy updated:

- -

Markolie updated:

- - -

17 January 2017

-

Crazylemon64 updated:

- -

Krausus updated:

- - -

16 January 2017

-

Alexshreds updated:

- -

Krausus updated:

- - -

15 January 2017

-

Funce updated:

- -

TullyBurnalot updated:

- - -

14 January 2017

-

Crazylemon64 updated:

- -

Krausus updated:

- - -

13 January 2017

-

DarkPyrolord updated:

- -

TullyBurnalot updated:

- -

uraniummeltdown updated:

- - -

12 January 2017

-

TullyBurnalot updated:

- - -

11 January 2017

-

Crazylemon64 updated:

- -

Lady Luck updated:

- -

Markolie updated:

- -

uraniummeltdown updated:

- - -

10 January 2017

-

Crazylemon updated:

- -

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

TullyBurnalot updated:

- -

pinatacolada updated:

- -

uraniummeltdown updated:

- - -

09 January 2017

-

Crazylemon64 updated:

- -

Kyep updated:

- - -

07 January 2017

-

Markolie updated:

- - -

06 January 2017

-

Crazylemon64 updated:

- -

Fethas updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- - -

03 January 2017

-

KasparoVy updated:

- -

Kyep updated:

- - -

02 January 2017

-

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- - -

01 January 2017

-

Kyep updated:

- - -

31 December 2016

-

Crazylemon64 updated:

- - -

30 December 2016

-

Crazylemon updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- - -

29 December 2016

-

Fethas updated:

- -

Markolie updated:

- - -

25 December 2016

-

FalseIncarnate updated:

- -

KasparoVy updated:

- - -

23 December 2016

-

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Markolie updated:

- - -

20 December 2016

-

TullyBurnalot updated:

- - -

14 December 2016

-

Fox McCloud updated:

- - -

13 December 2016

-

TullyBurnalot updated:

- - -

12 December 2016

-

Kyep updated:

- -

TullyBurnalot updated:

- - -

09 December 2016

-

KasparoVy updated:

- - -

08 December 2016

-

Kyep updated:

- - -

07 December 2016

-

Markolie updated:

- -

TullyBurnalot updated:

- - -

06 December 2016

-

Markolie updated:

- - -

05 December 2016

-

KasparoVy updated:

- - -

04 December 2016

-

KasparoVy updated:

- - -

03 December 2016

-

Kyep updated:

- - -

01 December 2016

-

Crazylemon64 updated:

- -

KasparoVy updated:

- -

Twinmold93 updated:

- - -

29 November 2016

-

KasparoVy updated:

- - -

28 November 2016

-

Allfd updated:

- -

Crazylemon64 updated:

- -

KasparoVy updated:

- - -

26 November 2016

-

Fox McCloud updated:

- - -

23 November 2016

-

Fethas updated:

- -

FlattestNerd updated:

- -

Fox McCloud updated:

- - -

22 November 2016

-

KasparoVy updated:

- -

Twinmold93 updated:

- - -

20 November 2016

-

KasparoVy updated:

- - -

15 November 2016

-

KasparoVy updated:

- - -

13 November 2016

-

Fox McCloud updated:

- - -

07 November 2016

-

Crazylemon updated:

- -

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Twinmold93 updated:

- - -

23 October 2016

-

KasparoVy updated:

- - -

15 October 2016

-

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

GeneralChaos81 updated:

- -

KasparoVy updated:

- -

Krausus updated:

- - -

16 September 2016

-

Aurorablade updated:

- -

Coldflame updated:

- -

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

FlattestGuitar updated:

- -

FreeStylaLT updated:

- -

IcyV updated:

- -

Improvedname updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

LittleBigKid2000 updated:

- -

TheDZD updated:

- -

Ty-Omaha updated:

- - -

01 September 2016

-

Crazylemon updated:

- -

monster860 updated:

- - -

29 August 2016

-

Crazylemon updated:

- -

DaveTheHeadcrab updated:

- -

Fox McCloud updated:

- -

Fox McCloud and KorPhaeron updated:

- -

FreeStylaLT updated:

- -

Krausus updated:

- -

Kyep updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- - -

19 August 2016

-

Crazylemon updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

IcyV updated:

- -

Krausus updated:

- -

TheDZD updated:

- - -

18 August 2016

-

Alexshreds updated:

- -

Ar3nn updated:

- -

Chakishreds updated:

- -

Chopchop1614 updated:

- -

Crazylemon updated:

- -

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

General Chaos updated:

- -

IcyV updated:

- -

KorPhaeron and Fox McCloud updated:

- -

Krausus updated:

- -

Kyep updated:

- -

LittleBigKid2000 updated:

- -

Norgad updated:

- -

TullyBurnalot updated:

- -

Ty-Omaha updated:

- -

taukausanake updated:

- -

tristan1333 updated:

- - -

08 August 2016

-

Ar3nn updated:

- -

Chakirski updated:

- -

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

LittleBigKid2000 updated:

- -

Pinatacolada updated:

- -

Ty-Omaha updated:

- -

Yurivw updated:

- -

chopchop1614 updated:

- - -

03 August 2016

-

Fox McCloud updated:

- -

Krausus updated:

- -

chopchop1614 updated:

- - -

01 August 2016

-

Crazylemon64 updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

IcyV updated:

- -

Krausus updated:

- -

Kyep updated:

- -

TheBeoni updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- -

Twinmold updated:

- -

tigercat2000 updated:

- - -

28 July 2016

-

A Giant-Ass Mountain of Salt updated:

- -

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

Fox-McCloud updated:

- -

FreeStylaLT updated:

- -

Krausus updated:

- -

Kyep updated:

- -

Spacemanspark updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- -

Twinmold updated:

- -

Ty-Omaha updated:

- -

monster860, clusterfack, and DeityLink updated:

- -

tigercat2000 updated:

- - -

21 July 2016

-

Alffd updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Kyep updated:

- -

TullyBBurnalot updated:

- -

monster860 updated:

- - -

20 July 2016

-

Allfd updated:

- -

Ar3nn updated:

- -

Chakirski updated:

- -

CrAzYPiLoT updated:

- -

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

LittleBigKid2000 and TullyBBurnalot updated:

- -

Tauka Usanake updated:

- -

TullyBBurnalot updated:

- -

TullyBurnalot updated:

- -

Twinmold updated:

- -

monster860 updated:

- -

tigercat2000 updated:

- - -

13 July 2016

-

Ar3nn updated:

- -

Chakirski updated:

- -

DaveTheHeadcrab updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- -

Twinmold updated:

- -

monster860 updated:

- - -

09 July 2016

-

Chakirski updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Krausus updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- -

Twinmold updated:

- - -

07 July 2016

-

Ar3nn updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

TheDZD updated:

- -

TullyBurnalot updated:

- - -

02 July 2016

-

Fethas updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

VampyrBytes updated:

- -

monster860 updated:

- -

tigercat2000 updated:

- - -

28 June 2016

-

CrAzYPiLoT updated:

- -

DaveTheHeadcrab updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Kyep updated:

- -

LittleBigKid2000 updated:

- -

Norgad updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

Twinmold updated:

- -

VampyrBytes updated:

- -

monster860 updated:

- -

tigercat2000 updated:

- -

tkdrg updated:

- - -

19 June 2016

-

DaveTheHeadcrab updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

KasparoVy updated:

- -

LittleBigKid2000 updated:

- -

TheDZD updated:

- -

Twinmold updated:

- -

monster860 updated:

- -

tkdrg, Delimusca, Aranclanos, TheDZD updated:

- - -

12 June 2016

-

CrAzYPiLoT updated:

- -

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

Fox McCloud updated:

- -

FreeStylaLT updated:

- -

Glorken updated:

- -

KasparoVy updated:

- -

Krausus updated:

- -

Many -tg-station Coders, TheDZD updated:

- -

Spacemanspark updated:

- -

Tauka Usanake updated:

- -

Twinmold updated:

- -

monster860 updated:

- -

pinatacolada updated:

- - -

02 June 2016

-

CrAzYPiLoT updated:

- -

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

Fox McCloud updated:

- -

IK3I updated:

- -

Tastyfish updated:

- -

monster860 updated:

- - -

31 May 2016

-

CrAzYPiLoT updated:

- -

Fox McCloud updated:

- -

QuinnAggeler updated:

- -

Tastyfish updated:

- -

monster860 updated:

- - -

25 May 2016

-

CrAzYPiLoT updated:

- -

Fox McCloud updated:

- -

IK3I updated:

- - -

24 May 2016

-

CrAzYPiLoT updated:

- -

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

QuinnAggeler updated:

- -

Tastyfish updated:

- -

monster860 updated:

- - -

20 May 2016

-

Fox McCloud updated:

- -

KasparoVy updated:

- -

QuinnAggeler updated:

- -

monster860 updated:

- - -

17 May 2016

-

AugRob updated:

- -

Fox MCCloud updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

monster860 updated:

- - -

10 May 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

IK3I updated:

- -

NTSAM updated:

- -

QuinnAggeler updated:

- -

tigercat2000 updated:

- - -

04 May 2016

-

CrAzYPiLoT updated:

- -

Crazylemon64 updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

IK3I updated:

- -

KasparoVy updated:

- -

LittleBigKid2000 updated:

- -

Norgad updated:

- -

QuinnAggeler updated:

- -

pinatacolada updated:

- - -

25 April 2016

-

CrAzYPiLoT updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

Meisaka updated:

- -

tigercat2000 updated:

- - -

20 April 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

MarsM0nd updated:

- -

Tastyfish updated:

- -

monster860 updated:

- -

tigercat2000 updated:

- - -

14 April 2016

-

Aurorablade updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Meisaka updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

monster860 updated:

- - -

12 April 2016

-

FalseIncarnate updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Tastyfish updated:

- - -

07 April 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Tastyfish updated:

- -

tigercat2000 updated:

- - -

02 April 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

ProperPants updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

tigercat2000 updated:

- - -

26 March 2016

-

Fox McCloud updated:

- -

KasparoVy updated:

- -

TheDZD updated:

- - -

25 March 2016

-

Crazylemon64 updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- - -

22 March 2016

-

Crazylemon64 updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Regens updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

TravellingMerchant updated:

- -

tigercat2000 updated:

- - -

16 March 2016

-

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

Tastyfish updated:

- -

monster860 updated:

- -

taukausanake updated:

- - -

08 March 2016

-

Crazylemon64 updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Spacemanspark updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- -

monster860 updated:

- - -

07 March 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Tastyfish updated:

- -

VampyrBytes updated:

- - -

26 February 2016

-

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Spacemanspark updated:

- -

Tastyfish updated:

- - -

24 February 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

FlattestGuitar updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

PPI updated:

- -

Regen1 updated:

- -

Spacemanspark updated:

- -

Tastyfish updated:

- -

pinatacolada updated:

- -

ppi updated:

- - -

13 February 2016

-

Crazylemon64 updated:

- -

DaveTheHeadcrab updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

TheDZD updated:

- - -

10 February 2016

-

Crazylemon64 updated:

- -

Fox McCloud updated:

- -

Glorken updated:

- -

KasparoVy updated:

- -

PPI updated:

- -

Regen updated:

- -

Spacemanspark updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- - -

31 January 2016

-

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

Tastyfish updated:

- -

pinatacolada updated:

- - -

29 January 2016

-

Crazylemon updated:

- -

Crazylemon64 updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- - -

24 January 2016

-

KasparoVy updated:

- -

Tastyfish updated:

- - -

23 January 2016

-

Fox McCloud updated:

- -

Tastyfish updated:

- -

Tigerbat2000 updated:

- - -

20 January 2016

-

DarkPyrolord updated:

- -

FalseIncarnate updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Tastyfish updated:

- -

TheDZD updated:

- - -

18 January 2016

-

Crazylemon updated:

- -

Dave The Headcrab updated:

- -

Deanthelis updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

Jey updated:

- -

KasparoVy updated:

- -

Kyep updated:

- -

NTSAM updated:

- -

PPI updated:

- -

Tastyfish updated:

- -

Tigercat2000 updated:

- - -

13 January 2016

-

Fox McCloud updated:

- -

Kyep updated:

- -

Tastyfish updated:

- - -

11 January 2016

-

CrAzYPiLoT updated:

- -

Crazylemon updated:

- -

Dave The Headcrab updated:

- -

Fox McCloud updated:

- - -

08 January 2016

-

Crazylemon updated:

- -

Dave The Headcrab updated:

- -

FalseIncarnate updated:

- -

Fethas updated:

- -

Fox McCloud updated:

- -

KasparoVy updated:

- -

Tastyfish updated:

- -

Tigercat2000 updated:

- - -

06 January 2016

-

Certhic updated:

- -

Crazylemon64 updated:

- -

Tastyfish updated:

- - -

03 January 2016

-

TheDZD updated:

- -
- -GoonStation 13 Development Team -
- Coders: Stuntwaffle, Showtime, Pantaloons, Nannek, Keelin, Exadv1, hobnob, Justicefries, 0staf, sniperchance, AngriestIBM, BrianOBlivion
- Spriters: Supernorn, Haruhi, Stuntwaffle, Pantaloons, Rho, SynthOrange, I Said No
-
-
-

Creative Commons License
Except where otherwise noted, Goon Station 13 is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.
Rights are currently extended to SomethingAwful Goons only.

-

Some icons by Yusuke Kamiyamane. All rights reserved. Licensed under a Creative Commons Attribution 3.0 License.

- - - diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index bd6ac9e988b..51b62a2d299 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -6352,3 +6352,6 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - rscadd: Slimes can steal nutrition from other slimes when attacking them - rscadd: Slime docility potion no longer makes a simple animal slime, instead makes the target docile and never hungry +2018-04-28: + MINIMAN10000: + - rscadd: Developer express start diff --git a/html/changelogs/AutoChangeLog-pr-8933.yml b/html/changelogs/AutoChangeLog-pr-8933.yml new file mode 100644 index 00000000000..49ecc55e737 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8933.yml @@ -0,0 +1,3 @@ +author: Fox McCloud +changes: [] +delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-8936.yml b/html/changelogs/AutoChangeLog-pr-8936.yml new file mode 100644 index 00000000000..c4907653f89 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8936.yml @@ -0,0 +1,4 @@ +author: "Fox McCloud" +delete-after: True +changes: + - rscadd: "Adds Blast cannon" diff --git a/html/changelogs/AutoChangeLog-pr-8941.yml b/html/changelogs/AutoChangeLog-pr-8941.yml new file mode 100644 index 00000000000..da88bb3f516 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8941.yml @@ -0,0 +1,3 @@ +author: Anasari +changes: [] +delete-after: true diff --git a/html/changelogs/AutoChangeLog-pr-8957.yml b/html/changelogs/AutoChangeLog-pr-8957.yml new file mode 100644 index 00000000000..49ecc55e737 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8957.yml @@ -0,0 +1,3 @@ +author: Fox McCloud +changes: [] +delete-after: true diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index e465cdd6901..16eeff1107f 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi index b258ecdfb50..2066f60075a 100644 Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi index fb256e34937..9ed3a2ca4bf 100644 Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ diff --git a/icons/mob/species/plasmaman/helmet.dmi b/icons/mob/species/plasmaman/helmet.dmi index bc47b78d03f..188db97e788 100644 Binary files a/icons/mob/species/plasmaman/helmet.dmi and b/icons/mob/species/plasmaman/helmet.dmi differ diff --git a/icons/mob/species/plasmaman/suit.dmi b/icons/mob/species/plasmaman/suit.dmi index f61a5a0e805..42832013f85 100644 Binary files a/icons/mob/species/plasmaman/suit.dmi and b/icons/mob/species/plasmaman/suit.dmi differ diff --git a/icons/obj/clothing/species/plasmaman/hats.dmi b/icons/obj/clothing/species/plasmaman/hats.dmi index bcfa32a558e..1d85d184ad1 100644 Binary files a/icons/obj/clothing/species/plasmaman/hats.dmi and b/icons/obj/clothing/species/plasmaman/hats.dmi differ diff --git a/icons/obj/clothing/species/plasmaman/suits.dmi b/icons/obj/clothing/species/plasmaman/suits.dmi index 86f4e6ede2e..43d69f5fa82 100644 Binary files a/icons/obj/clothing/species/plasmaman/suits.dmi and b/icons/obj/clothing/species/plasmaman/suits.dmi differ diff --git a/icons/obj/custom_items.dmi b/icons/obj/custom_items.dmi index 384b608a5c2..8c3b77c337d 100644 Binary files a/icons/obj/custom_items.dmi and b/icons/obj/custom_items.dmi differ diff --git a/icons/obj/guns/projectile.dmi b/icons/obj/guns/projectile.dmi index a630f68652d..22dc1384904 100644 Binary files a/icons/obj/guns/projectile.dmi and b/icons/obj/guns/projectile.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 8c3d0f2c81f..2e1e5d45006 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/paradise.dme b/paradise.dme index da357ca690e..6e65eb0a362 100644 --- a/paradise.dme +++ b/paradise.dme @@ -200,7 +200,6 @@ #include "code\controllers\Processes\obj.dm" #include "code\controllers\Processes\shuttles.dm" #include "code\controllers\Processes\ticker.dm" -#include "code\controllers\Processes\timer.dm" #include "code\controllers\Processes\weather.dm" #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" @@ -212,6 +211,7 @@ #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\sun.dm" #include "code\controllers\subsystem\throwing.dm" +#include "code\controllers\subsystem\timer.dm" #include "code\datums\action.dm" #include "code\datums\ai_law_sets.dm" #include "code\datums\ai_laws.dm" @@ -312,6 +312,8 @@ #include "code\datums\helper_datums\map_template.dm" #include "code\datums\helper_datums\teleport.dm" #include "code\datums\helper_datums\topic_input.dm" +#include "code\datums\looping_sounds\looping_sound.dm" +#include "code\datums\looping_sounds\machinery_sounds.dm" #include "code\datums\outfits\outfit.dm" #include "code\datums\outfits\outfit_admin.dm" #include "code\datums\ruins\space.dm" @@ -2002,6 +2004,7 @@ #include "code\modules\projectiles\guns\energy\telegun.dm" #include "code\modules\projectiles\guns\magic\staff.dm" #include "code\modules\projectiles\guns\magic\wand.dm" +#include "code\modules\projectiles\guns\misc\blastcannon.dm" #include "code\modules\projectiles\guns\projectile\automatic.dm" #include "code\modules\projectiles\guns\projectile\bow.dm" #include "code\modules\projectiles\guns\projectile\launchers.dm" diff --git a/sound/machines/shower/shower_end.ogg b/sound/machines/shower/shower_end.ogg new file mode 100644 index 00000000000..80b93af39eb Binary files /dev/null and b/sound/machines/shower/shower_end.ogg differ diff --git a/sound/machines/shower/shower_mid1.ogg b/sound/machines/shower/shower_mid1.ogg new file mode 100644 index 00000000000..e1ae5a0c456 Binary files /dev/null and b/sound/machines/shower/shower_mid1.ogg differ diff --git a/sound/machines/shower/shower_mid2.ogg b/sound/machines/shower/shower_mid2.ogg new file mode 100644 index 00000000000..4a54acd3524 Binary files /dev/null and b/sound/machines/shower/shower_mid2.ogg differ diff --git a/sound/machines/shower/shower_mid3.ogg b/sound/machines/shower/shower_mid3.ogg new file mode 100644 index 00000000000..8b4776a9b97 Binary files /dev/null and b/sound/machines/shower/shower_mid3.ogg differ diff --git a/sound/machines/shower/shower_start.ogg b/sound/machines/shower/shower_start.ogg new file mode 100644 index 00000000000..e5529f401bb Binary files /dev/null and b/sound/machines/shower/shower_start.ogg differ diff --git a/sound/weapons/blastcannon.ogg b/sound/weapons/blastcannon.ogg new file mode 100644 index 00000000000..9b88f3ecf96 Binary files /dev/null and b/sound/weapons/blastcannon.ogg differ