Merge remote-tracking branch 'citadel/master' into ex_act_3
This commit is contained in:
@@ -69,7 +69,7 @@ GLOBAL_LIST_EMPTY(actionspeed_modification_cache)
|
||||
return TRUE
|
||||
remove_actionspeed_modifier(existing, FALSE)
|
||||
if(length(actionspeed_modification))
|
||||
BINARY_INSERT(type_or_datum.id, actionspeed_modification, datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
|
||||
BINARY_INSERT(type_or_datum.id, actionspeed_modification, /datum/actionspeed_modifier, type_or_datum, priority, COMPARE_VALUE)
|
||||
LAZYSET(actionspeed_modification, type_or_datum.id, type_or_datum)
|
||||
if(update)
|
||||
update_actionspeed()
|
||||
|
||||
@@ -226,6 +226,14 @@
|
||||
key_cache[key] = 0
|
||||
return .
|
||||
|
||||
/proc/restore_stickybans()
|
||||
for (var/banned_ckey in GLOB.stickybanadmintexts)
|
||||
world.SetConfig("ban", banned_ckey, GLOB.stickybanadmintexts[banned_ckey])
|
||||
GLOB.stickybanadminexemptions = list()
|
||||
GLOB.stickybanadmintexts = list()
|
||||
if (GLOB.stickbanadminexemptiontimerid)
|
||||
deltimer(GLOB.stickbanadminexemptiontimerid)
|
||||
GLOB.stickbanadminexemptiontimerid = null
|
||||
|
||||
#undef STICKYBAN_MAX_MATCHES
|
||||
#undef STICKYBAN_MAX_EXISTING_USER_MATCHES
|
||||
|
||||
@@ -52,11 +52,9 @@
|
||||
edit_emitter(user)
|
||||
|
||||
/obj/effect/sound_emitter/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(check_rights_for(user.client, R_SOUNDS))
|
||||
activate(user)
|
||||
to_chat(user, "<span class='notice'>Sound emitter activated.</span>")
|
||||
return TRUE
|
||||
to_chat(user, "<span class='notice'>Sound emitter activated.</span>", confidential = TRUE)
|
||||
|
||||
/obj/effect/sound_emitter/proc/edit_emitter(mob/user)
|
||||
var/dat = ""
|
||||
@@ -84,20 +82,20 @@
|
||||
if(!new_label)
|
||||
return
|
||||
maptext = new_label
|
||||
to_chat(user, "<span class='notice'>Label set to [maptext].</span>")
|
||||
to_chat(user, "<span class='notice'>Label set to [maptext].</span>", confidential = TRUE)
|
||||
if(href_list["edit_sound_file"])
|
||||
var/new_file = input(user, "Choose a sound file.", "Sound Emitter") as null|sound
|
||||
if(!new_file)
|
||||
return
|
||||
sound_file = new_file
|
||||
to_chat(user, "<span class='notice'>New sound file set to [sound_file].</span>")
|
||||
to_chat(user, "<span class='notice'>New sound file set to [sound_file].</span>", confidential = TRUE)
|
||||
if(href_list["edit_volume"])
|
||||
var/new_volume = input(user, "Choose a volume.", "Sound Emitter", sound_volume) as null|num
|
||||
if(isnull(new_volume))
|
||||
return
|
||||
new_volume = clamp(new_volume, 0, 100)
|
||||
sound_volume = new_volume
|
||||
to_chat(user, "<span class='notice'>Volume set to [sound_volume]%.</span>")
|
||||
to_chat(user, "<span class='notice'>Volume set to [sound_volume]%.</span>", confidential = TRUE)
|
||||
if(href_list["edit_mode"])
|
||||
var/new_mode
|
||||
var/mode_list = list("Local (normal sound)" = SOUND_EMITTER_LOCAL, "Direct (not affected by environment/location)" = SOUND_EMITTER_DIRECT)
|
||||
@@ -105,7 +103,7 @@
|
||||
if(!new_mode)
|
||||
return
|
||||
motus_operandi = mode_list[new_mode]
|
||||
to_chat(user, "<span class='notice'>Mode set to [motus_operandi].</span>")
|
||||
to_chat(user, "<span class='notice'>Mode set to [motus_operandi].</span>", confidential = TRUE)
|
||||
if(href_list["edit_range"])
|
||||
var/new_range
|
||||
var/range_list = list("Radius (all mobs within a radius)" = SOUND_EMITTER_RADIUS, "Z-Level (all mobs on the same z)" = SOUND_EMITTER_ZLEVEL, "Global (all players)" = SOUND_EMITTER_GLOBAL)
|
||||
@@ -113,14 +111,14 @@
|
||||
if(!new_range)
|
||||
return
|
||||
emitter_range = range_list[new_range]
|
||||
to_chat(user, "<span class='notice'>Range set to [emitter_range].</span>")
|
||||
to_chat(user, "<span class='notice'>Range set to [emitter_range].</span>", confidential = TRUE)
|
||||
if(href_list["edit_radius"])
|
||||
var/new_radius = input(user, "Choose a radius.", "Sound Emitter", sound_volume) as null|num
|
||||
if(isnull(new_radius))
|
||||
return
|
||||
new_radius = clamp(new_radius, 0, 127)
|
||||
play_radius = new_radius
|
||||
to_chat(user, "<span class='notice'>Audible radius set to [play_radius].</span>")
|
||||
to_chat(user, "<span class='notice'>Audible radius set to [play_radius].</span>", confidential = TRUE)
|
||||
if(href_list["play"])
|
||||
activate(user)
|
||||
edit_emitter(user) //Refresh the UI to see our changes
|
||||
|
||||
+318
-55
@@ -7,7 +7,7 @@
|
||||
if ("add")
|
||||
var/list/ban = list()
|
||||
var/ckey
|
||||
ban["admin"] = usr.key
|
||||
ban["admin"] = usr.ckey
|
||||
ban["type"] = list("sticky")
|
||||
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
ban["ckey"] = ckey
|
||||
|
||||
if (get_stickyban_from_ckey(ckey))
|
||||
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (data["reason"])
|
||||
ban["message"] = data["reason"]
|
||||
@@ -31,7 +32,26 @@
|
||||
return
|
||||
ban["message"] = "[reason]"
|
||||
|
||||
if(SSdbcore.Connect()) // todo: second wave
|
||||
// var/datum/db_query/query_create_stickyban = SSdbcore.NewQuery({"
|
||||
// INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
|
||||
// VALUES (:ckey, :message, :banning_admin)
|
||||
// "}, list("ckey" = ckey, "message" = ban["message"], "banning_admin" = usr.ckey))
|
||||
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery({"
|
||||
INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin)
|
||||
VALUES ([ckey], [ban["message"]], [usr.ckey])
|
||||
"})
|
||||
if (query_create_stickyban.warn_execute())
|
||||
ban["fromdb"] = TRUE
|
||||
qdel(query_create_stickyban)
|
||||
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
ban = stickyban2list(list2stickyban(ban))
|
||||
ban["matches_this_round"] = list()
|
||||
ban["existing_user_matches_this_round"] = list()
|
||||
ban["admin_matches_this_round"] = list()
|
||||
ban["pending_matches_this_round"] = list()
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
log_admin_private("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
|
||||
@@ -43,14 +63,29 @@
|
||||
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
if (alert("Are you sure you want to remove the sticky ban on [ckey]?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
if (!get_stickyban_from_ckey(ckey))
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
world.SetConfig("ban",ckey, null)
|
||||
SSstickyban.cache -= ckey
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// SSdbcore.QuerySelect(list(
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = :ckey", list("ckey" = ckey)),
|
||||
// SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = :ckey", list("ckey" = ckey))
|
||||
// ), warn = TRUE, qdel = TRUE)
|
||||
SSdbcore.QuerySelect(list(
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = [ckey]"),
|
||||
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = [ckey]")
|
||||
), warn = TRUE, qdel = TRUE)
|
||||
|
||||
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
|
||||
@@ -64,42 +99,45 @@
|
||||
var/alt = ckey(data["alt"])
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/found = 0
|
||||
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
|
||||
for (var/key in ban["keys"])
|
||||
if (ckey(key) == alt)
|
||||
found = 1
|
||||
break
|
||||
|
||||
if (!found)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>")
|
||||
var/key = LAZYACCESS(ban["keys"], alt)
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
|
||||
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
|
||||
//we have to do this again incase something changes
|
||||
ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
found = 0
|
||||
for (var/key in ban["keys"])
|
||||
if (ckey(key) == alt)
|
||||
ban["keys"] -= key
|
||||
found = 1
|
||||
break
|
||||
key = LAZYACCESS(ban["keys"], alt)
|
||||
|
||||
if (!found)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>")
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
LAZYREMOVE(ban["keys"], alt)
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_remove_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery(
|
||||
"DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
)
|
||||
query_remove_stickyban_alt.warn_execute()
|
||||
qdel(query_remove_stickyban_alt)
|
||||
|
||||
log_admin_private("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
|
||||
|
||||
@@ -109,7 +147,7 @@
|
||||
var/ckey = data["ckey"]
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
var/oldreason = ban["message"]
|
||||
var/reason = input(usr,"Reason","Reason","[ban["message"]]") as text|null
|
||||
@@ -118,28 +156,203 @@
|
||||
//we have to do this again incase something changed while we waited for input
|
||||
ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
ban["message"] = "[reason]"
|
||||
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_edit_stickyban = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban")] SET reason = :reason WHERE ckey = :ckey",
|
||||
// list("reason" = reason, "ckey" = ckey)
|
||||
// )
|
||||
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban")] SET reason = [reason] WHERE ckey = [ckey]"
|
||||
)
|
||||
query_edit_stickyban.warn_execute()
|
||||
qdel(query_edit_stickyban)
|
||||
|
||||
log_admin_private("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
|
||||
|
||||
if ("exempt")
|
||||
if (!data["ckey"])
|
||||
return
|
||||
var/ckey = data["ckey"]
|
||||
if (!data["alt"])
|
||||
return
|
||||
var/alt = ckey(data["alt"])
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/key = LAZYACCESS(ban["keys"], alt)
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
|
||||
//we have to do this again incase something changes
|
||||
ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
key = LAZYACCESS(ban["keys"], alt)
|
||||
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s link to [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
LAZYREMOVE(ban["keys"], alt)
|
||||
key["exempt"] = TRUE
|
||||
LAZYSET(ban["whitelist"], alt, key)
|
||||
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_exempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
)
|
||||
query_exempt_stickyban_alt.warn_execute()
|
||||
qdel(query_exempt_stickyban_alt)
|
||||
|
||||
log_admin_private("[key_name(usr)] has exempted [alt] from [ckey]'s sticky ban")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has exempted [alt] from [ckey]'s sticky ban</span>")
|
||||
|
||||
if ("unexempt")
|
||||
if (!data["ckey"])
|
||||
return
|
||||
var/ckey = data["ckey"]
|
||||
if (!data["alt"])
|
||||
return
|
||||
var/alt = ckey(data["alt"])
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/key = LAZYACCESS(ban["whitelist"], alt)
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not exempt from [ckey]'s sticky ban!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
|
||||
//we have to do this again incase something changes
|
||||
ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
key = LAZYACCESS(ban["whitelist"], alt)
|
||||
if (!key)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
LAZYREMOVE(ban["whitelist"], alt)
|
||||
key["exempt"] = FALSE
|
||||
LAZYSET(ban["keys"], alt, key)
|
||||
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
|
||||
SSstickyban.cache[ckey] = ban
|
||||
|
||||
if (SSdbcore.Connect())
|
||||
// var/datum/db_query/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
// "UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = :ckey AND matched_ckey = :alt",
|
||||
// list("ckey" = ckey, "alt" = alt)
|
||||
// )
|
||||
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery(
|
||||
"UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = [ckey] AND matched_ckey = [alt]"
|
||||
)
|
||||
query_unexempt_stickyban_alt.warn_execute()
|
||||
qdel(query_unexempt_stickyban_alt)
|
||||
|
||||
log_admin_private("[key_name(usr)] has unexempted [alt] from [ckey]'s sticky ban")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has unexempted [alt] from [ckey]'s sticky ban</span>")
|
||||
|
||||
if ("timeout")
|
||||
if (!data["ckey"])
|
||||
return
|
||||
if (!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
var/ckey = data["ckey"]
|
||||
|
||||
if (alert("Are you sure you want to put [ckey]'s stickyban on timeout until next round (or removed)?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
|
||||
ban["timeout"] = TRUE
|
||||
|
||||
world.SetConfig("ban", ckey, null)
|
||||
|
||||
var/cachedban = SSstickyban.cache[ckey]
|
||||
if (cachedban)
|
||||
cachedban["timeout"] = TRUE
|
||||
|
||||
log_admin_private("[key_name(usr)] has put [ckey]'s sticky ban on timeout.")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [ckey]'s sticky ban on timeout.</span>")
|
||||
|
||||
if ("untimeout")
|
||||
if (!data["ckey"])
|
||||
return
|
||||
if (!SSdbcore.Connect())
|
||||
to_chat(usr, "<span class='adminnotice'>No database connection!</span>", confidential = TRUE)
|
||||
return
|
||||
var/ckey = data["ckey"]
|
||||
|
||||
if (alert("Are you sure you want to lift the timeout on [ckey]'s stickyban?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
var/cachedban = SSstickyban.cache[ckey]
|
||||
if (cachedban)
|
||||
cachedban["timeout"] = FALSE
|
||||
if (!ban)
|
||||
if (!cachedban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
ban = cachedban
|
||||
|
||||
ban["timeout"] = FALSE
|
||||
|
||||
world.SetConfig("ban",ckey,list2stickyban(ban))
|
||||
|
||||
log_admin_private("[key_name(usr)] has taken [ckey]'s sticky ban off of timeout.")
|
||||
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has taken [ckey]'s sticky ban off of timeout.</span>")
|
||||
|
||||
|
||||
if ("revert")
|
||||
if (!data["ckey"])
|
||||
return
|
||||
var/ckey = data["ckey"]
|
||||
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start?","Are you sure","Yes","No") == "No")
|
||||
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start (or last edit)?","Are you sure","Yes","No") == "No")
|
||||
return
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
return
|
||||
var/cached_ban = SSstickyban.cache[ckey]
|
||||
if (!cached_ban)
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>")
|
||||
to_chat(usr, "<span class='adminnotice'>Error: No cached sticky ban for [ckey] found!</span>", confidential = TRUE)
|
||||
world.SetConfig("ban",ckey,null)
|
||||
|
||||
log_admin_private("[key_name(usr)] has reverted [ckey]'s sticky ban to its state at round start.")
|
||||
@@ -150,14 +363,22 @@
|
||||
world.SetConfig("ban",ckey,list2stickyban(cached_ban))
|
||||
|
||||
|
||||
/datum/admins/proc/stickyban_gethtml(ckey, ban)
|
||||
. = {"
|
||||
/datum/admins/proc/stickyban_gethtml(ckey)
|
||||
var/ban = get_stickyban_from_ckey(ckey)
|
||||
if (!ban)
|
||||
return
|
||||
var/timeout
|
||||
if (SSdbcore.Connect())
|
||||
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=[(ban["timeout"] ? "untimeout" : "timeout")]&ckey=[ckey]'>\[[(ban["timeout"] ? "untimeout" : "timeout" )]\]</a>"
|
||||
else
|
||||
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>"
|
||||
. = list({"
|
||||
<a href='?_src_=holder;[HrefToken()];stickyban=remove&ckey=[ckey]'>\[-\]</a>
|
||||
<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>
|
||||
[timeout]
|
||||
<b>[ckey]</b>
|
||||
<br />"
|
||||
[ban["message"]] <b><a href='?_src_=holder;[HrefToken()];stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />
|
||||
"}
|
||||
"})
|
||||
if (ban["admin"])
|
||||
. += "[ban["admin"]]<br />"
|
||||
else
|
||||
@@ -166,19 +387,24 @@
|
||||
for (var/key in ban["keys"])
|
||||
if (ckey(key) == ckey)
|
||||
continue
|
||||
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
|
||||
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=exempt&ckey=[ckey]&alt=[ckey(key)]'>\[E\]</a></li>"
|
||||
|
||||
for (var/key in ban["whitelist"])
|
||||
if (ckey(key) == ckey)
|
||||
continue
|
||||
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=unexempt&ckey=[ckey]&alt=[ckey(key)]'>\[UE\]</a></li>"
|
||||
|
||||
. += "</ol>\n"
|
||||
|
||||
/datum/admins/proc/stickyban_show()
|
||||
if(!check_rights(R_BAN))
|
||||
return
|
||||
var/list/bans = sortList(world.GetConfig("ban"))
|
||||
var/banhtml = ""
|
||||
var/list/bans = sticky_banned_ckeys()
|
||||
var/list/banhtml = list()
|
||||
for(var/key in bans)
|
||||
var/ckey = ckey(key)
|
||||
var/ban = stickyban2list(world.GetConfig("ban",key))
|
||||
banhtml += "<br /><hr />\n"
|
||||
banhtml += stickyban_gethtml(ckey,ban)
|
||||
banhtml += stickyban_gethtml(ckey)
|
||||
|
||||
var/html = {"
|
||||
<head>
|
||||
@@ -186,22 +412,49 @@
|
||||
</head>
|
||||
<body>
|
||||
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;[HrefToken()];stickyban=add'>\[+\]</a><br>
|
||||
[banhtml]
|
||||
[banhtml.Join("")]
|
||||
</body>
|
||||
"}
|
||||
usr << browse(html,"window=stickybans;size=700x400")
|
||||
|
||||
/proc/get_stickyban_from_ckey(var/ckey)
|
||||
/proc/sticky_banned_ckeys()
|
||||
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
|
||||
if (SSstickyban.dbcacheexpire < world.time)
|
||||
SSstickyban.Populatedbcache()
|
||||
if (SSstickyban.dbcacheexpire)
|
||||
return SSstickyban.dbcache.Copy()
|
||||
|
||||
return sortList(world.GetConfig("ban"))
|
||||
|
||||
|
||||
/proc/get_stickyban_from_ckey(ckey)
|
||||
. = list()
|
||||
if (!ckey)
|
||||
return null
|
||||
ckey = ckey(ckey)
|
||||
. = null
|
||||
for (var/key in world.GetConfig("ban"))
|
||||
if (ckey(key) == ckey)
|
||||
. = stickyban2list(world.GetConfig("ban",key))
|
||||
break
|
||||
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
|
||||
if (SSstickyban.dbcacheexpire < world.time)
|
||||
SSstickyban.Populatedbcache()
|
||||
if (SSstickyban.dbcacheexpire)
|
||||
. = SSstickyban.dbcache[ckey]
|
||||
//reset the cache incase its a newer ban (but only if we didn't update the cache recently)
|
||||
if (!. && SSstickyban.dbcacheexpire != world.time+STICKYBAN_DB_CACHE_TIME)
|
||||
SSstickyban.dbcacheexpire = 1
|
||||
SSstickyban.Populatedbcache()
|
||||
. = SSstickyban.dbcache[ckey]
|
||||
if (.)
|
||||
var/list/cachedban = SSstickyban.cache["[ckey]"]
|
||||
if (cachedban)
|
||||
.["timeout"] = cachedban["timeout"]
|
||||
|
||||
/proc/stickyban2list(var/ban)
|
||||
.["fromdb"] = TRUE
|
||||
return
|
||||
|
||||
. = stickyban2list(world.GetConfig("ban", ckey)) || stickyban2list(world.GetConfig("ban", ckey(ckey))) || list()
|
||||
|
||||
if (!length(.))
|
||||
return null
|
||||
|
||||
/proc/stickyban2list(ban, strictdb = TRUE)
|
||||
if (!ban)
|
||||
return null
|
||||
. = params2list(ban)
|
||||
@@ -212,30 +465,40 @@
|
||||
var/ckey = ckey(key)
|
||||
ckeys[ckey] = ckey //to make searching faster.
|
||||
.["keys"] = ckeys
|
||||
if (.["whitelist"])
|
||||
var/keys = splittext(.["whitelist"], ",")
|
||||
var/ckeys = list()
|
||||
for (var/key in keys)
|
||||
var/ckey = ckey(key)
|
||||
ckeys[ckey] = ckey //to make searching faster.
|
||||
.["whitelist"] = ckeys
|
||||
.["type"] = splittext(.["type"], ",")
|
||||
.["IP"] = splittext(.["IP"], ",")
|
||||
.["computer_id"] = splittext(.["computer_id"], ",")
|
||||
. -= "fromdb"
|
||||
|
||||
|
||||
/proc/list2stickyban(var/list/ban)
|
||||
/proc/list2stickyban(list/ban)
|
||||
if (!ban || !islist(ban))
|
||||
return null
|
||||
. = ban.Copy()
|
||||
if (.["keys"])
|
||||
.["keys"] = jointext(.["keys"], ",")
|
||||
if (.["IP"])
|
||||
.["IP"] = jointext(.["IP"], ",")
|
||||
if (.["computer_id"])
|
||||
.["computer_id"] = jointext(.["computer_id"], ",")
|
||||
if (.["whitelist"])
|
||||
.["whitelist"] = jointext(.["whitelist"], ",")
|
||||
if (.["type"])
|
||||
.["type"] = jointext(.["type"], ",")
|
||||
|
||||
//internal tracking only, shouldn't be stored
|
||||
. -= "reverting"
|
||||
. -= "matches_this_round"
|
||||
. -= "existing_user_matches_this_round"
|
||||
. -= "admin_matches_this_round"
|
||||
. -= "matches_this_round"
|
||||
. -= "reverting"
|
||||
. -= "pending_matches_this_round"
|
||||
|
||||
//storing these can sometimes cause sticky bans to start matching everybody
|
||||
// and isn't even needed for sticky ban matching, as the hub tracks these separately
|
||||
. -= "IP"
|
||||
. -= "computer_id"
|
||||
|
||||
. = list2params(.)
|
||||
|
||||
|
||||
@@ -782,7 +782,7 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
|
||||
if(temp.vars.Find(v))
|
||||
temp.vars[v] = SDQL_expression(d, set_list[sets])
|
||||
else
|
||||
temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
|
||||
temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]), TRUE)
|
||||
break
|
||||
if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)))
|
||||
temp = temp.vars[v]
|
||||
|
||||
@@ -158,3 +158,19 @@
|
||||
else
|
||||
to_chat(src, "Failed to move mob to a valid location.", confidential = TRUE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Send Mob") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/// Proc to hook user-enacted teleporting behavior and keep logging of the event.
|
||||
/atom/movable/proc/admin_teleport(atom/new_location, message = TRUE)
|
||||
if(isnull(new_location))
|
||||
log_admin("[key_name(usr)] teleported [key_name(src)] to nullspace")
|
||||
moveToNullspace()
|
||||
else
|
||||
log_admin("[key_name(usr)] teleported [key_name(src)] to [AREACOORD(loc)]")
|
||||
forceMove(new_location)
|
||||
|
||||
/mob/admin_teleport(atom/new_location, message = TRUE)
|
||||
var/msg = "[key_name_admin(usr)] teleported [ADMIN_LOOKUPFLW(src)] to [isnull(new_location) ? "nullspace" : ADMIN_VERBOSEJMP(loc)]"
|
||||
if(message)
|
||||
message_admins(msg)
|
||||
admin_ticket_log(src, msg)
|
||||
return ..()
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
if(!check_rights(R_SOUNDS))
|
||||
return
|
||||
|
||||
var/freq = 1
|
||||
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
|
||||
if(!vol)
|
||||
return
|
||||
var/freq = input(usr, "What frequency would you like the sound to play at?",, 1) as null|num
|
||||
if(!freq)
|
||||
freq = 1
|
||||
vol = clamp(vol, 1, 100)
|
||||
|
||||
var/sound/admin_sound = new()
|
||||
@@ -18,14 +16,14 @@
|
||||
admin_sound.channel = CHANNEL_ADMIN
|
||||
admin_sound.frequency = freq
|
||||
admin_sound.wait = 1
|
||||
admin_sound.repeat = 0
|
||||
admin_sound.repeat = FALSE
|
||||
admin_sound.status = SOUND_STREAM
|
||||
admin_sound.volume = vol
|
||||
|
||||
var/res = alert(usr, "Show the title of this song to the players?",, "Yes","No", "Cancel")
|
||||
switch(res)
|
||||
if("Yes")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>")
|
||||
to_chat(world, "<span class='boldannounce'>An admin played: [S]</span>", confidential = TRUE)
|
||||
if("Cancel")
|
||||
return
|
||||
|
||||
@@ -49,7 +47,7 @@
|
||||
|
||||
log_admin("[key_name(src)] played a local sound [S]")
|
||||
message_admins("[key_name_admin(src)] played a local sound [S]")
|
||||
playsound(get_turf(src.mob), S, 50, 0, 0)
|
||||
playsound(get_turf(src.mob), S, 50, FALSE, FALSE)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Play Local Sound") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/play_web_sound()
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
if (!thing)
|
||||
continue
|
||||
var/datum/D = thing
|
||||
if (D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE)
|
||||
if (D.vv_edit_var(variable, initial(D.vars[variable]), TRUE) != FALSE)
|
||||
accepted++
|
||||
else
|
||||
rejected++
|
||||
@@ -135,7 +135,7 @@
|
||||
for(var/V in varsvars)
|
||||
new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]")
|
||||
|
||||
if (D.vv_edit_var(variable, new_value) != FALSE)
|
||||
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
|
||||
accepted++
|
||||
else
|
||||
rejected++
|
||||
@@ -161,7 +161,7 @@
|
||||
if(many && !new_value)
|
||||
new_value = new type()
|
||||
|
||||
if (D.vv_edit_var(variable, new_value) != FALSE)
|
||||
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
|
||||
accepted++
|
||||
else
|
||||
rejected++
|
||||
@@ -176,7 +176,7 @@
|
||||
if (!thing)
|
||||
continue
|
||||
var/datum/D = thing
|
||||
if (D.vv_edit_var(variable, new_value) != FALSE)
|
||||
if (D.vv_edit_var(variable, new_value, TRUE) != FALSE)
|
||||
accepted++
|
||||
else
|
||||
rejected++
|
||||
|
||||
@@ -109,7 +109,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
set name = "Find References"
|
||||
set src in world
|
||||
|
||||
find_references(FALSE)
|
||||
find_references_legacy(FALSE)
|
||||
|
||||
|
||||
/datum/proc/find_references_legacy(skip_alert)
|
||||
@@ -164,7 +164,7 @@ GLOBAL_LIST_EMPTY(deletion_failures)
|
||||
|
||||
qdel(src, TRUE) //force a qdel
|
||||
if(!running_find_references)
|
||||
find_references(TRUE)
|
||||
find_references_legacy(TRUE)
|
||||
|
||||
|
||||
/datum/verb/qdel_then_if_fail_find_references()
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
var/nightime_duration = 900 //15 Minutes
|
||||
|
||||
/obj/effect/sunlight/Initialize()
|
||||
countdown()
|
||||
hud_tick()
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/countdown)
|
||||
INVOKE_ASYNC(src, .proc/hud_tick)
|
||||
|
||||
/obj/effect/sunlight/proc/countdown()
|
||||
set waitfor = FALSE
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
/datum/antagonist/vassal/greet()
|
||||
to_chat(owner, "<span class='userdanger'>You are now the mortal servant of [master.owner.current], a bloodsucking vampire!</span>")
|
||||
to_chat(owner, "<span class='boldannounce'>The power of [master.owner.current.p_their()] immortal blood compells you to obey [master.owner.current.p_them()] in all things, even offering your own life to prolong theirs.<br>\
|
||||
You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotransen do not apply to you now; only your vampiric master's word must be obeyed.<span>")
|
||||
You are not required to obey any other Bloodsucker, for only [master.owner.current] is your master. The laws of Nanotrasen do not apply to you now; only your vampiric master's word must be obeyed.<span>")
|
||||
// Effects...
|
||||
owner.current.playsound_local(null, 'sound/magic/mutate.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
//owner.store_memory("You became the mortal servant of [master.owner.current], a bloodsucking vampire!")
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
if(canrespec)
|
||||
to_chat(owner.current, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
|
||||
reset_powers()
|
||||
playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5, soundenvwet = 0)
|
||||
playsound(get_turf(owner.current), 'sound/effects/lingreadapt.ogg', 75, TRUE, 5)
|
||||
canrespec = 0
|
||||
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
|
||||
return 1
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
for(var/obj/machinery/light/L in range(4, user))
|
||||
L.on = 1
|
||||
L.break_light_tube()
|
||||
playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
|
||||
playsound(get_turf(user), 'sound/effects/lingscreech.ogg', 75, TRUE, 5)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/proc_holder/changeling/dissonant_shriek
|
||||
@@ -49,5 +49,5 @@
|
||||
L.on = 1
|
||||
L.break_light_tube()
|
||||
empulse_using_range(get_turf(user), 8, TRUE)
|
||||
playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5, soundenvwet = 0)
|
||||
playsound(get_turf(user), 'sound/effects/lingempscreech.ogg', 75, TRUE, 5)
|
||||
return TRUE
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.z == z)
|
||||
if(get_dist(src, M) >= 7)
|
||||
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE, falloff = 10)
|
||||
M.playsound_local(src, 'sound/magic/blink.ogg', 10, FALSE)
|
||||
else
|
||||
M.playsound_local(src, 'sound/magic/blink.ogg', 50, FALSE)
|
||||
|
||||
|
||||
@@ -15,3 +15,5 @@
|
||||
audible_message("<i>*click*</i>")
|
||||
playsound(src, 'sound/items/screwdriver2.ogg', 50, TRUE)
|
||||
activate()
|
||||
|
||||
. = ..()
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
alpha = 75
|
||||
|
||||
/obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech/Crossed(atom/movable/AM)
|
||||
|
||||
. = ..()
|
||||
if(!istype(AM,/obj/mecha/))
|
||||
return
|
||||
|
||||
|
||||
@@ -21,3 +21,5 @@
|
||||
if(isliving(AM) && opacity)
|
||||
var/mob/living/L = AM
|
||||
L.adjust_fire_stacks(-1) //It's wet!
|
||||
return
|
||||
. = ..()
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
master.update_icon()
|
||||
|
||||
/obj/item/assembly_holder/Crossed(atom/movable/AM as mob|obj)
|
||||
. = ..()
|
||||
if(a_left)
|
||||
a_left.Crossed(AM)
|
||||
if(a_right)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
/// Process asset cache client topic calls for "asset_cache_confirm_arrival=[INT]"
|
||||
/// Process asset cache client topic calls for `"asset_cache_confirm_arrival=[INT]"`
|
||||
/client/proc/asset_cache_confirm_arrival(job_id)
|
||||
var/asset_cache_job = round(text2num(job_id))
|
||||
//because we skip the limiter, we have to make sure this is a valid arrival and not somebody tricking us into letting them append to a list without limit.
|
||||
@@ -10,7 +10,7 @@
|
||||
return asset_cache_job || TRUE
|
||||
|
||||
|
||||
/// Process asset cache client topic calls for "asset_cache_preload_data=[HTML+JSON_STRING]
|
||||
/// Process asset cache client topic calls for `"asset_cache_preload_data=[HTML+JSON_STRING]"`
|
||||
/client/proc/asset_cache_preload_data(data)
|
||||
var/json = data
|
||||
var/list/preloaded_assets = json_decode(json)
|
||||
|
||||
@@ -47,8 +47,9 @@
|
||||
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
|
||||
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
|
||||
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
|
||||
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif'
|
||||
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif'
|
||||
// "borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
|
||||
// "robotact.gif" = 'icons/program_icons/robotact.gif'
|
||||
)
|
||||
|
||||
/datum/asset/simple/radar_assets
|
||||
@@ -156,7 +157,6 @@
|
||||
)
|
||||
|
||||
/datum/asset/simple/namespaced/fontawesome
|
||||
legacy = TRUE
|
||||
assets = list(
|
||||
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
|
||||
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
|
||||
@@ -248,6 +248,10 @@
|
||||
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
|
||||
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
|
||||
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
|
||||
"ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
|
||||
"fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
|
||||
"rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
|
||||
"voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
|
||||
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
|
||||
"assistant" = 'icons/UI_Icons/Achievements/Mafia/assistant.png',
|
||||
"changeling" = 'icons/UI_Icons/Achievements/Mafia/changeling.png',
|
||||
@@ -288,7 +292,7 @@
|
||||
)
|
||||
|
||||
/datum/asset/spritesheet/simple/pills
|
||||
name ="pills"
|
||||
name = "pills"
|
||||
assets = list(
|
||||
"pill1" = 'icons/UI_Icons/Pills/pill1.png',
|
||||
"pill2" = 'icons/UI_Icons/Pills/pill2.png',
|
||||
@@ -313,7 +317,28 @@
|
||||
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
|
||||
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
|
||||
)
|
||||
|
||||
/*
|
||||
/datum/asset/spritesheet/simple/condiments
|
||||
name = "condiments"
|
||||
assets = list(
|
||||
CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
|
||||
"enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
|
||||
"flour" = 'icons/UI_Icons/Condiments/flour.png',
|
||||
"mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
|
||||
"milk" = 'icons/UI_Icons/Condiments/milk.png',
|
||||
"blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
|
||||
"rice" = 'icons/UI_Icons/Condiments/rice.png',
|
||||
"sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
|
||||
"soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
|
||||
"soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
|
||||
"sugar" = 'icons/UI_Icons/Condiments/sugar.png',
|
||||
"ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
|
||||
"capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
|
||||
"frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
|
||||
"bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
|
||||
"cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
|
||||
)
|
||||
*/
|
||||
//this exists purely to avoid meta by pre-loading all language icons.
|
||||
/datum/asset/language/register()
|
||||
for(var/path in typesof(/datum/language))
|
||||
@@ -484,3 +509,31 @@
|
||||
/datum/asset/spritesheet/mafia/register()
|
||||
InsertAll("", 'icons/obj/mafia.dmi')
|
||||
..()
|
||||
|
||||
/datum/asset/simple/portraits
|
||||
var/tab = "use subtypes of this please"
|
||||
assets = list()
|
||||
|
||||
/datum/asset/simple/portraits/New()
|
||||
if(!SSpersistence.paintings || !SSpersistence.paintings[tab] || !length(SSpersistence.paintings[tab]))
|
||||
return
|
||||
for(var/p in SSpersistence.paintings[tab])
|
||||
var/list/portrait = p
|
||||
var/png = "data/paintings/[tab]/[portrait["md5"]].png"
|
||||
if(fexists(png))
|
||||
assets[portrait["title"]] = png
|
||||
..() //this is where it registers all these assets we added to the list
|
||||
|
||||
/datum/asset/simple/portraits/library
|
||||
tab = "library"
|
||||
|
||||
/datum/asset/simple/portraits/library_secure
|
||||
tab = "library_secure"
|
||||
|
||||
/datum/asset/simple/portraits/library_private
|
||||
tab = "library_private"
|
||||
|
||||
/datum/asset/simple/safe
|
||||
assets = list(
|
||||
"safe_dial.png" = 'html/safe_dial.png'
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ Call .get_url_mappings() to get an associated list with the urls your assets can
|
||||
|
||||
See the documentation for `/datum/asset_transport` for the backend api the asset datums utilize.
|
||||
|
||||
The global variable `SSassets.transport` contains the currently configured transport.
|
||||
The global variable `SSassets.transport` contains the currently configured transport.
|
||||
|
||||
|
||||
|
||||
@@ -32,6 +32,6 @@ The global variable `SSassets.transport` contains the currently configured trans
|
||||
|
||||
Because byond browse() calls use non-blocking queues, if your code uses output() (which bypasses all of these queues) to invoke javascript functions you will need to first have the javascript announce to the server it has loaded before trying to invoke js functions.
|
||||
|
||||
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
|
||||
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
|
||||
|
||||
CSS files that use url() can be made to use the CDN without needing to rewrite all url() calls in code by using the namespaced helper datum. See the documentation for `/datum/asset/simple/namespaced` for details.
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
SSair.add_to_active(T)
|
||||
return ..()
|
||||
|
||||
/// Function for Extools Atmos
|
||||
/turf/proc/update_air_ref()
|
||||
|
||||
/////////////////GAS MIXTURE PROCS///////////////////
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#define AMMO_DROP_LIFETIME 300
|
||||
#define CTF_REQUIRED_PLAYERS 4
|
||||
|
||||
|
||||
|
||||
/obj/item/ctf
|
||||
name = "banner"
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
@@ -16,13 +18,13 @@
|
||||
righthand_file = 'icons/mob/inhands/equipment/banners_righthand.dmi'
|
||||
desc = "A banner with Nanotrasen's logo on it."
|
||||
slowdown = 2
|
||||
item_flags = SLOWS_WHILE_IN_HAND
|
||||
throw_speed = 0
|
||||
throw_range = 1
|
||||
force = 200
|
||||
armour_penetration = 1000
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
anchored = TRUE
|
||||
item_flags = SLOWS_WHILE_IN_HAND
|
||||
var/team = WHITE_TEAM
|
||||
var/reset_cooldown = 0
|
||||
var/anyonecanpickup = TRUE
|
||||
@@ -53,12 +55,13 @@
|
||||
to_chat(M, "<span class='userdanger'>\The [src] has been returned to base!</span>")
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/ctf/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/item/ctf/on_attack_hand(mob/living/user)
|
||||
if(!is_ctf_target(user) && !anyonecanpickup)
|
||||
to_chat(user, "Non players shouldn't be moving the flag!")
|
||||
to_chat(user, "<span class='warning'>Non-players shouldn't be moving the flag!</span>")
|
||||
return
|
||||
if(team in user.faction)
|
||||
to_chat(user, "You can't move your own flag!")
|
||||
to_chat(user, "<span class='warning'>You can't move your own flag!</span>")
|
||||
return
|
||||
if(loc == user)
|
||||
if(!user.dropItemToGround(src))
|
||||
@@ -68,7 +71,7 @@
|
||||
if(!user.put_in_active_hand(src))
|
||||
dropped(user)
|
||||
return
|
||||
user.anchored = TRUE
|
||||
user.set_anchored(TRUE)
|
||||
user.status_flags &= ~CANPUSH
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
@@ -79,7 +82,7 @@
|
||||
|
||||
/obj/item/ctf/dropped(mob/user)
|
||||
..()
|
||||
user.anchored = FALSE
|
||||
user.set_anchored(FALSE)
|
||||
user.status_flags |= CANPUSH
|
||||
reset_cooldown = world.time + 200 //20 seconds
|
||||
START_PROCESSING(SSobj, src)
|
||||
@@ -172,20 +175,20 @@
|
||||
GLOB.poi_list.Remove(src)
|
||||
..()
|
||||
|
||||
/obj/machinery/capture_the_flag/process()
|
||||
/obj/machinery/capture_the_flag/process(delta_time)
|
||||
for(var/i in spawned_mobs)
|
||||
if(!i)
|
||||
spawned_mobs -= i
|
||||
continue
|
||||
// Anyone in crit, automatically reap
|
||||
var/mob/living/M = i
|
||||
if(M.InCritical() || M.stat == DEAD)
|
||||
ctf_dust_old(M)
|
||||
var/mob/living/living_participant = i
|
||||
if(living_participant.InCritical() || living_participant.stat == DEAD)
|
||||
ctf_dust_old(living_participant)
|
||||
else
|
||||
// The changes that you've been hit with no shield but not
|
||||
// instantly critted are low, but have some healing.
|
||||
M.adjustBruteLoss(-5)
|
||||
M.adjustFireLoss(-5)
|
||||
living_participant.adjustBruteLoss(-2.5 * delta_time)
|
||||
living_participant.adjustFireLoss(-2.5 * delta_time)
|
||||
|
||||
/obj/machinery/capture_the_flag/red
|
||||
name = "Red CTF Controller"
|
||||
@@ -212,6 +215,10 @@
|
||||
toggle_all_ctf(user)
|
||||
return
|
||||
|
||||
|
||||
// if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
|
||||
// to_chat(user, "<span class='warning'>CTF has been temporarily disabled by admins.</span>")
|
||||
// return
|
||||
people_who_want_to_play |= user.ckey
|
||||
var/num = people_who_want_to_play.len
|
||||
var/remaining = CTF_REQUIRED_PLAYERS - num
|
||||
@@ -227,7 +234,7 @@
|
||||
return
|
||||
if(user.ckey in team_members)
|
||||
if(user.ckey in recently_dead_ckeys)
|
||||
to_chat(user, "It must be more than [DisplayTimeText(respawn_cooldown)] from your last death to respawn!")
|
||||
to_chat(user, "<span class='warning'>It must be more than [DisplayTimeText(respawn_cooldown)] from your last death to respawn!</span>")
|
||||
return
|
||||
var/client/new_team_member = user.client
|
||||
if(user.mind && user.mind.current)
|
||||
@@ -239,10 +246,10 @@
|
||||
if(CTF == src || CTF.ctf_enabled == FALSE)
|
||||
continue
|
||||
if(user.ckey in CTF.team_members)
|
||||
to_chat(user, "No switching teams while the round is going!")
|
||||
to_chat(user, "<span class='warning'>No switching teams while the round is going!</span>")
|
||||
return
|
||||
if(CTF.team_members.len < src.team_members.len)
|
||||
to_chat(user, "[src.team] has more team members than [CTF.team]. Try joining [CTF.team] team to even things up.")
|
||||
to_chat(user, "<span class='warning'>[src.team] has more team members than [CTF.team]! Try joining [CTF.team] team to even things up.</span>")
|
||||
return
|
||||
team_members |= user.ckey
|
||||
var/client/new_team_member = user.client
|
||||
@@ -258,7 +265,7 @@
|
||||
addtimer(CALLBACK(src, .proc/clear_cooldown, body.ckey), respawn_cooldown, TIMER_UNIQUE)
|
||||
body.dust()
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/clear_cooldown(var/ckey)
|
||||
/obj/machinery/capture_the_flag/proc/clear_cooldown(ckey)
|
||||
recently_dead_ckeys -= ckey
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/spawn_team_member(client/new_team_member)
|
||||
@@ -270,7 +277,7 @@
|
||||
M.equipOutfit(ctf_gear)
|
||||
M.dna.species.punchdamagehigh = 25
|
||||
M.dna.species.punchdamagelow = 25
|
||||
M.AddElement(/datum/element/ghost_role_eligibility)
|
||||
M.AddElement(/datum/element/ghost_role_eligibility) //??
|
||||
spawned_mobs += M
|
||||
|
||||
/obj/machinery/capture_the_flag/Topic(href, href_list)
|
||||
@@ -293,14 +300,15 @@
|
||||
victory()
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/victory()
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
for(var/mob/_competitor in GLOB.mob_living_list)
|
||||
var/mob/living/competitor = _competitor
|
||||
var/area/mob_area = get_area(competitor)
|
||||
if(istype(mob_area, /area/ctf))
|
||||
to_chat(M, "<span class='narsie [team_span]'>[team] team wins!</span>")
|
||||
to_chat(M, "<span class='userdanger'>Teams have been cleared. Click on the machines to vote to begin another round.</span>")
|
||||
for(var/obj/item/ctf/W in M)
|
||||
M.dropItemToGround(W)
|
||||
M.dust()
|
||||
to_chat(competitor, "<span class='narsie [team_span]'>[team] team wins!</span>")
|
||||
to_chat(competitor, "<span class='userdanger'>Teams have been cleared. Click on the machines to vote to begin another round.</span>")
|
||||
for(var/obj/item/ctf/W in competitor)
|
||||
competitor.dropItemToGround(W)
|
||||
competitor.dust()
|
||||
for(var/obj/machinery/control_point/control in GLOB.machines)
|
||||
control.icon_state = "dominator"
|
||||
control.controlling = null
|
||||
@@ -328,7 +336,7 @@
|
||||
|
||||
dead_barricades.Cut()
|
||||
|
||||
notify_ghosts("[name] has been activated!", enter_link="<a href=?src=[REF(src)];join=1>(Click to join the [team] team!)</a> or click on the controller directly!", source = src, action=NOTIFY_ATTACK)
|
||||
notify_ghosts("[name] has been activated!", enter_link="<a href=?src=[REF(src)];join=1>(Click to join the [team] team!)</a> or click on the controller directly!", source = src, action=NOTIFY_ATTACK, header = "CTF has been activated")
|
||||
|
||||
if(!arena_reset)
|
||||
reset_the_arena()
|
||||
@@ -355,10 +363,10 @@
|
||||
ctf_enabled = FALSE
|
||||
arena_reset = FALSE
|
||||
var/area/A = get_area(src)
|
||||
for(var/i in GLOB.mob_list)
|
||||
var/mob/M = i
|
||||
if((get_area(A) == A) && (M.ckey in team_members))
|
||||
M.dust()
|
||||
for(var/_competitor in GLOB.mob_living_list)
|
||||
var/mob/living/competitor = _competitor
|
||||
if((get_area(A) == A) && (competitor.ckey in team_members))
|
||||
competitor.dust()
|
||||
team_members.Cut()
|
||||
spawned_mobs.Cut()
|
||||
recently_dead_ckeys.Cut()
|
||||
@@ -375,18 +383,18 @@
|
||||
CTF.ctf_gear = initial(ctf_gear)
|
||||
CTF.respawn_cooldown = DEFAULT_RESPAWN
|
||||
|
||||
/proc/ctf_floor_vanish(atom/target)
|
||||
if(isturf(target.loc))
|
||||
qdel(target)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf
|
||||
desc = "This looks like it could really hurt in melee."
|
||||
force = 75
|
||||
mag_type = /obj/item/ammo_box/magazine/m50/ctf
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped(mob/user)
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/dropped()
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
addtimer(CALLBACK(src, .proc/floor_vanish), 1)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle/ctf/proc/floor_vanish()
|
||||
if(isturf(loc))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/ammo_box/magazine/m50/ctf
|
||||
ammo_type = /obj/item/ammo_casing/a50/ctf
|
||||
@@ -400,6 +408,7 @@
|
||||
/obj/item/projectile/bullet/ctf/prehit(atom/target)
|
||||
if(is_ctf_target(target))
|
||||
damage = 60
|
||||
return //PROJECTILE_PIERCE_NONE /// hey uhh don't hit anyone behind them
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf
|
||||
@@ -407,16 +416,24 @@
|
||||
desc = "This looks like it could really hurt in melee."
|
||||
force = 50
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf/dropped(mob/user)
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf/dropped()
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
addtimer(CALLBACK(src, .proc/floor_vanish), 1)
|
||||
|
||||
/obj/item/gun/ballistic/automatic/laser/ctf/proc/floor_vanish()
|
||||
if(isturf(loc))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf
|
||||
ammo_type = /obj/item/ammo_casing/caseless/laser/ctf
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf/dropped(mob/user)
|
||||
/obj/item/ammo_box/magazine/recharge/ctf/dropped()
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
addtimer(CALLBACK(src, .proc/floor_vanish), 1)
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf/proc/floor_vanish()
|
||||
if(isturf(loc))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/ammo_casing/caseless/laser/ctf
|
||||
projectile_type = /obj/item/projectile/beam/ctf
|
||||
@@ -428,15 +445,16 @@
|
||||
/obj/item/projectile/beam/ctf/prehit(atom/target)
|
||||
if(is_ctf_target(target))
|
||||
damage = 150
|
||||
return //PROJECTILE_PIERCE_NONE /// hey uhhh don't hit anyone behind them
|
||||
. = ..()
|
||||
|
||||
/proc/is_ctf_target(atom/target)
|
||||
. = FALSE
|
||||
if(istype(target, /obj/structure/barricade/security/ctf))
|
||||
. = TRUE
|
||||
if(isliving(target))
|
||||
var/mob/living/H = target
|
||||
if((RED_TEAM in H.faction) || (BLUE_TEAM in H.faction))
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if(istype(H.wear_suit, /obj/item/clothing/suit/space/hardsuit/shielded/ctf))
|
||||
. = TRUE
|
||||
|
||||
// RED TEAM GUNS
|
||||
@@ -482,7 +500,11 @@
|
||||
|
||||
/obj/item/claymore/ctf/dropped(mob/user)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/ctf_floor_vanish, src), 1)
|
||||
addtimer(CALLBACK(src, .proc/floor_vanish), 1)
|
||||
|
||||
/obj/item/claymore/ctf/proc/floor_vanish()
|
||||
if(isturf(loc))
|
||||
qdel(src)
|
||||
|
||||
/datum/outfit/ctf
|
||||
name = "CTF"
|
||||
@@ -491,27 +513,28 @@
|
||||
suit = /obj/item/clothing/suit/space/hardsuit/shielded/ctf
|
||||
toggle_helmet = FALSE // see the whites of their eyes
|
||||
shoes = /obj/item/clothing/shoes/combat
|
||||
gloves = /obj/item/clothing/gloves/tackler/combat
|
||||
gloves = /obj/item/clothing/gloves/combat
|
||||
id = /obj/item/card/id/away
|
||||
belt = /obj/item/gun/ballistic/automatic/pistol/deagle/ctf
|
||||
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf
|
||||
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf
|
||||
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf
|
||||
back = /obj/item/claymore/ctf
|
||||
|
||||
/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
/datum/outfit/ctf/post_equip(mob/living/carbon/human/H, visualsOnly=FALSE)
|
||||
if(visualsOnly)
|
||||
return
|
||||
var/list/no_drops = list()
|
||||
var/obj/item/card/id/W = H.wear_id
|
||||
no_drops += W
|
||||
W.registered_name = H.real_name
|
||||
W.update_label(W.registered_name, W.assignment)
|
||||
W.update_label()
|
||||
|
||||
// The shielded hardsuit is already TRAIT_NODROP
|
||||
no_drops += H.get_item_by_slot(SLOT_GLOVES)
|
||||
no_drops += H.get_item_by_slot(SLOT_SHOES)
|
||||
no_drops += H.get_item_by_slot(SLOT_W_UNIFORM)
|
||||
no_drops += H.get_item_by_slot(SLOT_EARS)
|
||||
no_drops += H.get_item_by_slot(ITEM_SLOT_OCLOTHING)
|
||||
no_drops += H.get_item_by_slot(ITEM_SLOT_GLOVES)
|
||||
no_drops += H.get_item_by_slot(ITEM_SLOT_FEET)
|
||||
no_drops += H.get_item_by_slot(ITEM_SLOT_ICLOTHING)
|
||||
no_drops += H.get_item_by_slot(ITEM_SLOT_EARS)
|
||||
for(var/i in no_drops)
|
||||
var/obj/item/I = i
|
||||
ADD_TRAIT(I, TRAIT_NODROP, CAPTURE_THE_FLAG_TRAIT)
|
||||
@@ -525,6 +548,7 @@
|
||||
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/red
|
||||
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red
|
||||
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/red
|
||||
id = /obj/item/card/id/syndicate_command //it's red
|
||||
|
||||
/datum/outfit/ctf/red/instagib
|
||||
r_hand = /obj/item/gun/energy/laser/instakill/red
|
||||
@@ -535,12 +559,13 @@
|
||||
r_hand = /obj/item/gun/ballistic/automatic/laser/ctf/blue
|
||||
l_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue
|
||||
r_pocket = /obj/item/ammo_box/magazine/recharge/ctf/blue
|
||||
id = /obj/item/card/id/centcom //it's blue
|
||||
|
||||
/datum/outfit/ctf/blue/instagib
|
||||
r_hand = /obj/item/gun/energy/laser/instakill/blue
|
||||
shoes = /obj/item/clothing/shoes/jackboots/fast
|
||||
|
||||
/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
/datum/outfit/ctf/red/post_equip(mob/living/carbon/human/H)
|
||||
..()
|
||||
var/obj/item/radio/R = H.ears
|
||||
R.set_frequency(FREQ_CTF_RED)
|
||||
@@ -548,7 +573,7 @@
|
||||
R.independent = TRUE
|
||||
H.dna.species.stunmod = 0
|
||||
|
||||
/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
/datum/outfit/ctf/blue/post_equip(mob/living/carbon/human/H)
|
||||
..()
|
||||
var/obj/item/radio/R = H.ears
|
||||
R.set_frequency(FREQ_CTF_BLUE)
|
||||
@@ -595,6 +620,10 @@
|
||||
/obj/structure/barricade/security/ctf/make_debris()
|
||||
new /obj/effect/ctf/dead_barricade(get_turf(src))
|
||||
|
||||
/obj/structure/table/reinforced/ctf
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
flags_1 = NODECONSTRUCT_1
|
||||
|
||||
/obj/effect/ctf
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
@@ -617,10 +646,11 @@
|
||||
QDEL_IN(src, AMMO_DROP_LIFETIME)
|
||||
|
||||
/obj/effect/ctf/ammo/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
reload(AM)
|
||||
|
||||
/obj/effect/ctf/ammo/Bump(atom/movable/AM)
|
||||
reload(AM)
|
||||
/obj/effect/ctf/ammo/Bump(atom/A)
|
||||
reload(A)
|
||||
|
||||
/obj/effect/ctf/ammo/Bumped(atom/movable/AM)
|
||||
reload(AM)
|
||||
@@ -636,7 +666,7 @@
|
||||
qdel(G)
|
||||
O.equip(M)
|
||||
to_chat(M, "<span class='notice'>Ammunition reloaded!</span>")
|
||||
playsound(get_turf(M), 'sound/weapons/shotgunpump.ogg', 50, 1, -1)
|
||||
playsound(get_turf(M), 'sound/weapons/shotgunpump.ogg', 50, TRUE, -1)
|
||||
qdel(src)
|
||||
break
|
||||
|
||||
@@ -667,18 +697,18 @@
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
var/obj/machinery/capture_the_flag/controlling
|
||||
var/team = "none"
|
||||
var/point_rate = 1
|
||||
var/point_rate = 0.5
|
||||
|
||||
/obj/machinery/control_point/process()
|
||||
/obj/machinery/control_point/process(delta_time)
|
||||
if(controlling)
|
||||
controlling.control_points += point_rate
|
||||
controlling.control_points += point_rate * delta_time
|
||||
if(controlling.control_points >= controlling.control_points_to_win)
|
||||
controlling.victory()
|
||||
|
||||
/obj/machinery/control_point/attackby(mob/user, params)
|
||||
capture(user)
|
||||
|
||||
/obj/machinery/control_point/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
/obj/machinery/control_point/on_attack_hand(mob/user)
|
||||
capture(user)
|
||||
|
||||
/obj/machinery/control_point/proc/capture(mob/user)
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
/obj/effect/mob_spawn/Initialize(mapload)
|
||||
. = ..()
|
||||
if(instant || (roundstart && (mapload || (SSticker && SSticker.current_state > GAME_STATE_SETTING_UP))))
|
||||
create()
|
||||
INVOKE_ASYNC(src, .proc/create)
|
||||
else if(ghost_usable)
|
||||
GLOB.poi_list |= src
|
||||
LAZYADD(GLOB.mob_spawners[job_description ? job_description : name], src)
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
var/triggered = 0
|
||||
|
||||
/obj/effect/meatgrinder/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
Bumped(AM)
|
||||
|
||||
/obj/effect/meatgrinder/Bumped(atom/movable/AM)
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
var/path = "sound/chatter/[phomeme]_[length].ogg"
|
||||
|
||||
playsound(loc, path,
|
||||
vol = 40, vary = 0, extrarange = 3, falloff = FALSE)
|
||||
vol = 40, vary = 0, extrarange = 3)
|
||||
|
||||
sleep((length + 1) * chatter_get_sleep_multiplier(phomeme))
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
var/datum/preferences/prefs = null
|
||||
var/last_turn = 0
|
||||
var/move_delay = 0
|
||||
var/last_move = 0
|
||||
var/area = null
|
||||
|
||||
/// Last time we Click()ed. No clicking twice in one tick!
|
||||
|
||||
@@ -147,6 +147,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
|
||||
/client/proc/handle_statpanel_click(list/href_list)
|
||||
var/atom/target = locate(href_list["statpanel_item_target"])
|
||||
if(!target)
|
||||
return
|
||||
Click(target, target.loc, null, "[href_list["statpanel_item_shiftclick"]?"shift=1;":null][href_list["statpanel_item_ctrlclick"]?"ctrl=1;":null]&alt=[href_list["statpanel_item_altclick"]?"alt=1;":null]", FALSE, "statpanel")
|
||||
|
||||
/client/proc/is_content_unlocked()
|
||||
@@ -496,7 +498,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
GLOB.directory -= ckey
|
||||
log_access("Logout: [key_name(src)]")
|
||||
GLOB.ahelp_tickets.ClientLogout(src)
|
||||
// SSserver_maint.UpdateHubStatus()
|
||||
SSserver_maint.UpdateHubStatus()
|
||||
if(credits)
|
||||
QDEL_LIST(credits)
|
||||
if(holder)
|
||||
@@ -642,7 +644,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
query_log_connection.Execute()
|
||||
qdel(query_log_connection)
|
||||
|
||||
// SSserver_maint.UpdateHubStatus()
|
||||
SSserver_maint.UpdateHubStatus()
|
||||
|
||||
if(new_player)
|
||||
player_age = -1
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
/obj/item/clothing/gloves/fingerless/pugilist/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
if(current_equipped_slot == SLOT_GLOVES)
|
||||
if(slot == SLOT_GLOVES)
|
||||
use_buffs(user, TRUE)
|
||||
wornonce = TRUE
|
||||
|
||||
|
||||
@@ -61,8 +61,9 @@
|
||||
if(!hood)
|
||||
to_chat(loc, "<span class='warning'>[src] seems to be missing its hood..</span>")
|
||||
return
|
||||
hood.atom_colours = atom_colours.Copy()
|
||||
hood.update_atom_colour()
|
||||
if(atom_colours)
|
||||
hood.atom_colours = atom_colours.Copy()
|
||||
hood.update_atom_colour()
|
||||
if(!suittoggled)
|
||||
if(ishuman(src.loc))
|
||||
var/mob/living/carbon/human/H = src.loc
|
||||
@@ -198,8 +199,9 @@
|
||||
if(!helmet)
|
||||
to_chat(H, "<span class='warning'>[src] seems to be missing its helmet..</span>")
|
||||
return
|
||||
helmet.atom_colours = atom_colours.Copy()
|
||||
helmet.update_atom_colour()
|
||||
if(atom_colours)
|
||||
helmet.atom_colours = atom_colours.Copy()
|
||||
helmet.update_atom_colour()
|
||||
if(!suittoggled)
|
||||
if(ishuman(src.loc))
|
||||
if(H.wear_suit != src)
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
log_admin_private("[key_name(usr)] cancelled event [name].")
|
||||
SSblackbox.record_feedback("tally", "event_admin_cancelled", 1, typepath)
|
||||
|
||||
/datum/round_event_control/proc/runEvent()
|
||||
/datum/round_event_control/proc/runEvent(random = FALSE)
|
||||
var/datum/round_event/E = new typepath()
|
||||
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
|
||||
E.control = src
|
||||
|
||||
@@ -340,6 +340,7 @@
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/spacevine/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(!isliving(AM))
|
||||
return
|
||||
for(var/datum/spacevine_mutation/SM in mutations)
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
new reward(get_turf(src))
|
||||
|
||||
/mob/living/carbon/human/dummy/travelling_trader/Initialize()
|
||||
..()
|
||||
. = ..() // return a hint you fuck
|
||||
add_atom_colour("#570d6b", FIXED_COLOUR_PRIORITY) //make them purple (otherworldly!)
|
||||
set_light(1, -0.7, "#AAD84B")
|
||||
ADD_TRAIT(src,TRAIT_PIERCEIMMUNE, "trader_pierce_immune") //don't let people take their blood
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
if(!F.check_variables() && !override_checks)
|
||||
QDEL_NULL(F)
|
||||
if(start_field && (F || override_checks))
|
||||
F.Initialize()
|
||||
F.begin_field()
|
||||
return F
|
||||
|
||||
/datum/proximity_monitor/advanced
|
||||
@@ -78,11 +78,11 @@
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/process_edge_turf(turf/T)
|
||||
|
||||
/datum/proximity_monitor/advanced/New()
|
||||
/datum/proximity_monitor/advanced/New(atom/_host, range, _ignore_if_not_on_turf = TRUE)
|
||||
if(requires_processing)
|
||||
START_PROCESSING(SSfields, src)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/Initialize()
|
||||
/datum/proximity_monitor/advanced/proc/begin_field()
|
||||
setup_field()
|
||||
post_setup_field()
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
var/atom/_host = host
|
||||
var/atom/new_host_loc = _host.loc
|
||||
if(last_host_loc != new_host_loc)
|
||||
recalculate_field()
|
||||
INVOKE_ASYNC(src, .proc/recalculate_field)
|
||||
|
||||
/datum/proximity_monitor/advanced/proc/post_setup_field()
|
||||
|
||||
@@ -302,7 +302,7 @@
|
||||
|
||||
/obj/item/multitool/field_debug/attack_self(mob/user)
|
||||
operating = !operating
|
||||
to_chat(user, "You turn [src] [operating? "on":"off"].")
|
||||
to_chat(user, "<span class='notice'>You turn [src] [operating? "on":"off"].</span>")
|
||||
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
|
||||
listeningTo = null
|
||||
if(!istype(current) && operating)
|
||||
@@ -312,13 +312,15 @@
|
||||
else if(!operating)
|
||||
QDEL_NULL(current)
|
||||
|
||||
/obj/item/multitool/field_debug/dropped(mob/user)
|
||||
/obj/item/multitool/field_debug/dropped()
|
||||
. = ..()
|
||||
if(listeningTo)
|
||||
UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
|
||||
listeningTo = null
|
||||
|
||||
/obj/item/multitool/field_debug/proc/on_mob_move()
|
||||
SIGNAL_HANDLER
|
||||
|
||||
check_turf(get_turf(src))
|
||||
|
||||
/obj/item/multitool/field_debug/process()
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
if(G.summoner && locate(/obj/effect/proc_holder/spell/aoe_turf/timestop) in G.summoner.mind.spell_list) //It would only make sense that a person's stand would also be immune.
|
||||
immune[G] = TRUE
|
||||
if(start)
|
||||
timestop()
|
||||
INVOKE_ASYNC(src, .proc/timestop)
|
||||
|
||||
/obj/effect/timestop/Destroy()
|
||||
qdel(chronofield)
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
/obj/effect/timestop/proc/timestop()
|
||||
target = get_turf(src)
|
||||
playsound(src, 'sound/magic/timeparadox2.ogg', 75, 1, -1)
|
||||
playsound(src, 'sound/magic/timeparadox2.ogg', 75, TRUE, -1)
|
||||
chronofield = make_field(/datum/proximity_monitor/advanced/timestop, list("current_range" = freezerange, "host" = src, "immune" = immune, "check_anti_magic" = check_anti_magic, "check_holy" = check_holy))
|
||||
QDEL_IN(src, duration)
|
||||
|
||||
@@ -112,6 +112,8 @@
|
||||
unfreeze_turf(T)
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/unfreeze_atom(atom/movable/A)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(A.throwing)
|
||||
unfreeze_throwing(A)
|
||||
if(isliving(A))
|
||||
@@ -128,12 +130,14 @@
|
||||
frozen_things -= A
|
||||
global_frozen_atoms -= A
|
||||
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/freeze_mecha(obj/mecha/M)
|
||||
M.completely_disabled = TRUE
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/unfreeze_mecha(obj/mecha/M)
|
||||
M.completely_disabled = FALSE
|
||||
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/freeze_throwing(atom/movable/AM)
|
||||
var/datum/thrownthing/T = AM.throwing
|
||||
T.paused = TRUE
|
||||
@@ -160,7 +164,7 @@
|
||||
/datum/proximity_monitor/advanced/timestop/process()
|
||||
for(var/i in frozen_mobs)
|
||||
var/mob/living/m = i
|
||||
m.Stun(20, 1, 1)
|
||||
m.Stun(20, ignore_canstun = TRUE)
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/setup_field_turf(turf/T)
|
||||
for(var/i in T.contents)
|
||||
@@ -168,6 +172,7 @@
|
||||
freeze_turf(T)
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/freeze_projectile(obj/item/projectile/P)
|
||||
P.paused = TRUE
|
||||
|
||||
@@ -176,18 +181,18 @@
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/freeze_mob(mob/living/L)
|
||||
frozen_mobs += L
|
||||
L.Stun(20, 1, 1)
|
||||
L.Stun(20, ignore_canstun = TRUE)
|
||||
ADD_TRAIT(L, TRAIT_MUTE, TIMESTOP_TRAIT)
|
||||
walk(L, 0) //stops them mid pathing even if they're stunimmune
|
||||
if(isanimal(L))
|
||||
var/mob/living/simple_animal/S = L
|
||||
S.toggle_ai(AI_OFF)
|
||||
if(ishostile(L))
|
||||
var/mob/living/simple_animal/hostile/H = L
|
||||
H.LoseTarget()
|
||||
if(ishostile(L))
|
||||
var/mob/living/simple_animal/hostile/H = L
|
||||
H.LoseTarget()
|
||||
|
||||
/datum/proximity_monitor/advanced/timestop/proc/unfreeze_mob(mob/living/L)
|
||||
L.AdjustStun(-20, 1, 1)
|
||||
L.AdjustStun(-20, ignore_canstun = TRUE)
|
||||
REMOVE_TRAIT(L, TRAIT_MUTE, TIMESTOP_TRAIT)
|
||||
frozen_mobs -= L
|
||||
if(isanimal(L))
|
||||
|
||||
@@ -24,11 +24,12 @@
|
||||
desc = "Get off my turf!"
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/CanPass(atom/movable/AM, turf/target)
|
||||
. = ..()
|
||||
if(parent)
|
||||
return parent.field_turf_canpass(AM, src, target)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_turf/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(parent)
|
||||
return parent.field_turf_crossed(AM, src)
|
||||
return TRUE
|
||||
@@ -48,11 +49,12 @@
|
||||
desc = "Edgy description here."
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/CanPass(atom/movable/AM, turf/target)
|
||||
. = ..()
|
||||
if(parent)
|
||||
return parent.field_edge_canpass(AM, src, target)
|
||||
return TRUE
|
||||
|
||||
/obj/effect/abstract/proximity_checker/advanced/field_edge/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(parent)
|
||||
return parent.field_edge_crossed(AM, src)
|
||||
return TRUE
|
||||
|
||||
@@ -139,10 +139,11 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
Show()
|
||||
|
||||
/obj/effect/hallucination/simple/Moved(atom/OldLoc, Dir)
|
||||
. = ..()
|
||||
Show()
|
||||
|
||||
/obj/effect/hallucination/simple/Destroy()
|
||||
if(target && target.client)
|
||||
if(target?.client)
|
||||
target.client.images.Remove(current_image)
|
||||
active = FALSE
|
||||
return ..()
|
||||
@@ -1093,6 +1094,7 @@ GLOBAL_LIST_INIT(hallucination_list, list(
|
||||
target.client.images += image
|
||||
|
||||
/obj/effect/hallucination/danger/lava/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(AM == target)
|
||||
target.adjustStaminaLoss(20)
|
||||
new /datum/hallucination/fire(target)
|
||||
|
||||
@@ -50,16 +50,18 @@
|
||||
var/cached_z
|
||||
/// I'm busy, don't move.
|
||||
var/busy = FALSE
|
||||
|
||||
var/static/blacklisted_items = typecacheof(list(
|
||||
/obj/effect,
|
||||
/obj/belly,
|
||||
/obj/mafia_game_board,
|
||||
/obj/docking_port,
|
||||
/obj/shapeshift_holder,
|
||||
/obj/screen))
|
||||
/obj/effect,
|
||||
/obj/belly,
|
||||
/obj/mafia_game_board,
|
||||
/obj/docking_port,
|
||||
/obj/shapeshift_holder,
|
||||
/obj/screen
|
||||
))
|
||||
|
||||
/mob/living/simple_animal/jacq/Initialize()
|
||||
..()
|
||||
. = ..() //fuck you jacq, return a hint you shit
|
||||
cached_z = z
|
||||
poof()
|
||||
|
||||
@@ -158,23 +160,18 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/jacq/proc/gender_check(mob/living/carbon/C)
|
||||
var/gender = "lamb"
|
||||
if(C)
|
||||
if(C.gender == MALE)
|
||||
gender = "laddie"
|
||||
if(C.gender == FEMALE)
|
||||
gender = "lassie"
|
||||
return gender
|
||||
. = "lamb"
|
||||
switch(C)
|
||||
if(MALE)
|
||||
. = "laddie"
|
||||
if(FEMALE)
|
||||
. = "lassie"
|
||||
|
||||
//Ye wee bugger, gerrout of it. Ye've nae tae enjoy reading the code fer mae secrets like.
|
||||
/mob/living/simple_animal/jacq/proc/chit_chat(mob/living/carbon/C)
|
||||
//Very important
|
||||
var/gender = gender_check(C)
|
||||
if(C)
|
||||
if(C.gender == MALE)
|
||||
gender = "laddie"
|
||||
if(C.gender == FEMALE)
|
||||
gender = "lassie"
|
||||
// it physicaly cannot fail*. Why is there a fucking dupe
|
||||
|
||||
if(!progression["[C.real_name]"] || !(progression["[C.real_name]"] & JACQ_HELLO))
|
||||
visible_message("<b>[src]</b> smiles ominously at [C], <span class='spooky'>\"Well halo there [gender]! Ah'm Jacqueline, tae great Pumpqueen, great tae meet ye.\"</span>")
|
||||
|
||||
@@ -82,5 +82,5 @@
|
||||
var/sound/music_played = sound(soundfile)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played)
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, S = music_played)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
@@ -578,6 +578,7 @@
|
||||
return FALSE
|
||||
|
||||
/obj/item/electronic_assembly/Moved(oldLoc, dir)
|
||||
. = ..()
|
||||
for(var/I in assembly_components)
|
||||
var/obj/item/integrated_circuit/IC = I
|
||||
IC.ext_moved(oldLoc, dir)
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
/datum/job/proc/announce_head(var/mob/living/carbon/human/H, var/channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels.
|
||||
if(H && GLOB.announcement_systems.len)
|
||||
//timer because these should come after the captain announcement
|
||||
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/addtimer, CALLBACK(pick(GLOB.announcement_systems), /obj/machinery/announcement_system/proc/announce, "NEWHEAD", H.real_name, H.job, channels), 1))
|
||||
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, .proc/_addtimer, CALLBACK(pick(GLOB.announcement_systems), /obj/machinery/announcement_system/proc/announce, "NEWHEAD", H.real_name, H.job, channels), 1))
|
||||
|
||||
//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
|
||||
/datum/job/proc/player_old_enough(client/C)
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
var/curr_z = text2num(dmmRegex.group[5])
|
||||
if(curr_z < z_lower || curr_z > z_upper)
|
||||
continue
|
||||
|
||||
|
||||
var/curr_x = text2num(dmmRegex.group[3])
|
||||
var/curr_y = text2num(dmmRegex.group[4])
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
if(width > right_width)
|
||||
for(var/i in 1 to lines)
|
||||
gridLines[i] = copytext(gridLines[i], 1, key_len * right_width)
|
||||
|
||||
|
||||
// during the actual load we're starting at the top and working our way down
|
||||
gridSet.ycrd += lines - 1
|
||||
|
||||
@@ -300,14 +300,14 @@
|
||||
//we do this after we load everything in. if we don't; we'll have weird atmos bugs regarding atmos adjacent turfs
|
||||
T.AfterChange(CHANGETURF_IGNORE_AIR)
|
||||
|
||||
if(did_expand)
|
||||
world.refresh_atmos_grid()
|
||||
|
||||
#ifdef TESTING
|
||||
if(turfsSkipped)
|
||||
testing("Skipped loading [turfsSkipped] default turfs")
|
||||
#endif
|
||||
|
||||
if(did_expand)
|
||||
world.refresh_atmos_grid()
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/parsed_map/proc/build_cache(no_changeturf, bad_paths=null)
|
||||
|
||||
@@ -8,6 +8,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
|
||||
throwforce = 0
|
||||
|
||||
/mob/dead/Initialize()
|
||||
SHOULD_CALL_PARENT(FALSE)
|
||||
if(flags_1 & INITIALIZED_1)
|
||||
stack_trace("Warning: [src]([type]) initialized multiple times!")
|
||||
flags_1 |= INITIALIZED_1
|
||||
@@ -68,14 +69,15 @@ INITIALIZE_IMMEDIATE(/mob/dead)
|
||||
set desc= "Jump to the other server"
|
||||
if(mob_transforming)
|
||||
return
|
||||
var/list/csa = CONFIG_GET(keyed_list/cross_server)
|
||||
var/list/our_id = CONFIG_GET(string/cross_comms_name)
|
||||
var/list/csa = CONFIG_GET(keyed_list/cross_server) - our_id
|
||||
var/pick
|
||||
switch(csa.len)
|
||||
if(0)
|
||||
remove_verb(src, /mob/dead/proc/server_hop)
|
||||
to_chat(src, "<span class='notice'>Server Hop has been disabled.</span>")
|
||||
if(1)
|
||||
pick = csa[0]
|
||||
pick = csa[1]
|
||||
else
|
||||
pick = input(src, "Pick a server to jump to", "Server Hop") as null|anything in csa
|
||||
|
||||
@@ -100,7 +102,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
|
||||
|
||||
winset(src, null, "command=.options") //other wise the user never knows if byond is downloading resources
|
||||
|
||||
C << link("[addr]?server_hop=[key]")
|
||||
C << link("[addr]")
|
||||
|
||||
/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister
|
||||
if (registered_z != new_z)
|
||||
|
||||
@@ -341,4 +341,4 @@
|
||||
|
||||
/datum/sprite_accessory/insect_fluff/witchwing
|
||||
name = "Witch Wing"
|
||||
icon_state = "witchwing"
|
||||
icon_state = "witchwing"
|
||||
|
||||
@@ -91,8 +91,8 @@
|
||||
Attach(M)
|
||||
|
||||
/obj/item/clothing/mask/facehugger/Crossed(atom/target)
|
||||
. = ..()
|
||||
HasProximity(target)
|
||||
return
|
||||
|
||||
/obj/item/clothing/mask/facehugger/on_found(mob/finder)
|
||||
if(stat == CONSCIOUS)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
real_name = "Unknown"
|
||||
icon = 'icons/mob/human.dmi'
|
||||
icon_state = "caucasian_m"
|
||||
appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE
|
||||
SET_APPEARANCE_FLAGS(KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE)
|
||||
|
||||
/mob/living/carbon/human/Initialize()
|
||||
add_verb(src, /mob/living/proc/mob_sleep)
|
||||
@@ -1028,7 +1028,7 @@
|
||||
return
|
||||
|
||||
stop_pulling()
|
||||
riding_datum.handle_vehicle_layer()
|
||||
riding_datum.handle_vehicle_layer(dir)
|
||||
riding_datum.fireman_carrying = fireman
|
||||
. = ..(target, force, check_loc)
|
||||
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
return
|
||||
return considering
|
||||
|
||||
/mob/living/carbon/human/movement_delay()
|
||||
. = ..()
|
||||
if (m_intent == MOVE_INTENT_WALK && HAS_TRAIT(src, TRAIT_SPEEDY_STEP))
|
||||
. -= 1.5
|
||||
|
||||
/mob/living/carbon/human/slip(knockdown_amount, obj/O, lube)
|
||||
if(HAS_TRAIT(src, TRAIT_NOSLIPALL))
|
||||
return 0
|
||||
|
||||
@@ -594,10 +594,18 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
|
||||
drunkenness = max(drunkenness - (drunkenness * 0.04), 0)
|
||||
if(drunkenness >= 6)
|
||||
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
|
||||
if(prob(25))
|
||||
slurring += 2
|
||||
jitteriness = max(jitteriness - 3, 0)
|
||||
// throw_alert("drunk", /atom/movable/screen/alert/drunk)
|
||||
if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING))
|
||||
adjustBruteLoss(-0.12, FALSE)
|
||||
adjustFireLoss(-0.06, FALSE)
|
||||
sound_environment_override = SOUND_ENVIRONMENT_PSYCHOTIC
|
||||
else
|
||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "drunk")
|
||||
clear_alert("drunk")
|
||||
sound_environment_override = SOUND_ENVIRONMENT_NONE
|
||||
|
||||
if(mind && (mind.assigned_role == "Scientist" || mind.assigned_role == "Research Director"))
|
||||
if(SSresearch.science_tech)
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
overlay_fullscreen("high", /obj/screen/fullscreen/high)
|
||||
throw_alert("high", /obj/screen/alert/high)
|
||||
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "high", /datum/mood_event/high)
|
||||
sound_environment_override = SOUND_ENVIRONMENT_DRUGGED
|
||||
else
|
||||
clear_fullscreen("high")
|
||||
clear_alert("high")
|
||||
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "high")
|
||||
sound_environment_override = SOUND_ENVIRONMENT_NONE
|
||||
|
||||
/mob/living/carbon/set_drugginess(amount)
|
||||
druggy = max(amount, 0)
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
|
||||
if(lying && !buckled && prob(getBruteLoss()*200/maxHealth))
|
||||
makeTrail(newloc, T, old_direction)
|
||||
|
||||
if(causes_dirt_buildup_on_floor && (movement_type & GROUND))
|
||||
dirt_buildup()
|
||||
|
||||
|
||||
@@ -911,10 +911,10 @@
|
||||
|
||||
if(!istype(apc) || QDELETED(apc) || apc.stat & BROKEN)
|
||||
to_chat(src, "<span class='danger'>Hack aborted. The designated APC no longer exists on the power network.</span>")
|
||||
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1)
|
||||
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, TRUE, ignore_walls = FALSE)
|
||||
else if(apc.aidisabled)
|
||||
to_chat(src, "<span class='danger'>Hack aborted. \The [apc] is no longer responding to our systems.</span>")
|
||||
playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 1)
|
||||
playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, TRUE, ignore_walls = FALSE)
|
||||
else
|
||||
malf_picker.processing_time += 10
|
||||
|
||||
@@ -923,7 +923,7 @@
|
||||
apc.locked = TRUE
|
||||
apc.coverlocked = TRUE
|
||||
|
||||
playsound(get_turf(src), 'sound/machines/ding.ogg', 50, 1)
|
||||
playsound(get_turf(src), 'sound/machines/ding.ogg', 50, TRUE, ignore_walls = FALSE)
|
||||
to_chat(src, "Hack complete. \The [apc] is now under your exclusive control.")
|
||||
apc.update_icon()
|
||||
|
||||
|
||||
@@ -37,23 +37,25 @@
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/cockroach/Crossed(var/atom/movable/AM)
|
||||
if(ismob(AM))
|
||||
if(isliving(AM))
|
||||
var/mob/living/A = AM
|
||||
if(A.mob_size > MOB_SIZE_SMALL && !(A.movement_type & FLYING))
|
||||
if(prob(squish_chance))
|
||||
A.visible_message("<span class='notice'>[A] squashed [src].</span>", "<span class='notice'>You squashed [src].</span>")
|
||||
adjustBruteLoss(1) //kills a normal cockroach
|
||||
else
|
||||
visible_message("<span class='notice'>[src] avoids getting crushed.</span>")
|
||||
else
|
||||
if(isstructure(AM))
|
||||
/mob/living/simple_animal/cockroach/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(isliving(AM))
|
||||
var/mob/living/A = AM
|
||||
if(A.mob_size > MOB_SIZE_SMALL && !(A.movement_type & FLYING))
|
||||
if(HAS_TRAIT(A, TRAIT_PACIFISM))
|
||||
A.visible_message("<span class='notice'>[A] carefully steps over [src].</span>", "<span class='notice'>You carefully step over [src] to avoid hurting it.</span>")
|
||||
return
|
||||
if(prob(squish_chance))
|
||||
AM.visible_message("<span class='notice'>[src] was crushed under [AM].</span>")
|
||||
adjustBruteLoss(1)
|
||||
A.visible_message("<span class='notice'>[A] squashed [src].</span>", "<span class='notice'>You squashed [src].</span>")
|
||||
adjustBruteLoss(1) //kills a normal cockroach
|
||||
else
|
||||
visible_message("<span class='notice'>[src] avoids getting crushed.</span>")
|
||||
else if(isstructure(AM))
|
||||
if(prob(squish_chance))
|
||||
AM.visible_message("<span class='notice'>[src] is crushed under [AM].</span>")
|
||||
adjustBruteLoss(1)
|
||||
else
|
||||
visible_message("<span class='notice'>[src] avoids getting crushed.</span>")
|
||||
|
||||
/mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
|
||||
return
|
||||
|
||||
@@ -263,7 +263,7 @@
|
||||
/mob/living/simple_animal/pet/dog/corgi/proc/place_on_head(obj/item/item_to_add, mob/user)
|
||||
|
||||
if(istype(item_to_add, /obj/item/grenade/plastic)) // last thing he ever wears, I guess
|
||||
item_to_add.afterattack(src,user,1)
|
||||
INVOKE_ASYNC(item_to_add, /obj/item.proc/afterattack, src, user, 1)
|
||||
return
|
||||
|
||||
if(inventory_head)
|
||||
@@ -271,13 +271,15 @@
|
||||
to_chat(user, "<span class='warning'>You can't put more than one hat on [src]!</span>")
|
||||
return
|
||||
if(!item_to_add)
|
||||
user.visible_message("[user] pets [src].","<span class='notice'>You rest your hand on [src]'s head for a moment.</span>")
|
||||
user.visible_message("<span class='notice'>[user] pets [src].</span>", "<span class='notice'>You rest your hand on [src]'s head for a moment.</span>")
|
||||
if(flags_1 & HOLOGRAM_1)
|
||||
return
|
||||
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, src, /datum/mood_event/pet_animal, src)
|
||||
return
|
||||
|
||||
if(user && !user.temporarilyRemoveItemFromInventory(item_to_add))
|
||||
to_chat(user, "<span class='warning'>\The [item_to_add] is stuck to your hand, you cannot put it on [src]'s head!</span>")
|
||||
return 0
|
||||
return
|
||||
|
||||
var/valid = FALSE
|
||||
if(ispath(item_to_add.dog_fashion, /datum/dog_fashion/head))
|
||||
@@ -287,11 +289,11 @@
|
||||
|
||||
if(valid)
|
||||
if(health <= 0)
|
||||
to_chat(user, "<span class ='notice'>There is merely a dull, lifeless look in [real_name]'s eyes as you put the [item_to_add] on [p_them()].</span>")
|
||||
to_chat(user, "<span class='notice'>There is merely a dull, lifeless look in [real_name]'s eyes as you put the [item_to_add] on [p_them()].</span>")
|
||||
else if(user)
|
||||
user.visible_message("[user] puts [item_to_add] on [real_name]'s head. [src] looks at [user] and barks once.",
|
||||
"<span class='notice'>You put [item_to_add] on [real_name]'s head. [src] gives you a peculiar look, then wags [p_their()] tail once and barks.</span>",
|
||||
"<span class='italics'>You hear a friendly-sounding bark.</span>")
|
||||
user.visible_message("<span class='notice'>[user] puts [item_to_add] on [real_name]'s head. [src] looks at [user] and barks once.</span>",
|
||||
"<span class='notice'>You put [item_to_add] on [real_name]'s head. [src] gives you a peculiar look, then wags [p_their()] tail once and barks.</span>",
|
||||
"<span class='hear'>You hear a friendly-sounding bark.</span>")
|
||||
item_to_add.forceMove(src)
|
||||
src.inventory_head = item_to_add
|
||||
update_corgi_fluff()
|
||||
@@ -361,7 +363,7 @@
|
||||
icon_state = "old_corgi"
|
||||
icon_living = "old_corgi"
|
||||
icon_dead = "old_corgi_dead"
|
||||
desc = "At a ripe old age of [record_age] Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP
|
||||
desc = "At a ripe old age of [record_age], Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP
|
||||
turns_per_move = 20
|
||||
RemoveElement(/datum/element/mob_holder, held_icon)
|
||||
AddElement(/datum/element/mob_holder, "old_corgi")
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
|
||||
|
||||
/obj/effect/snare/Crossed(AM as mob|obj)
|
||||
. = ..()
|
||||
if(isliving(AM) && spawner && spawner.summoner && AM != spawner && !spawner.hasmatchingsummoner(AM))
|
||||
to_chat(spawner.summoner, "<span class='danger'><B>[AM] has crossed surveillance snare, [name].</span></B>")
|
||||
var/list/guardians = spawner.summoner.hasparasites()
|
||||
|
||||
+3
@@ -219,6 +219,9 @@
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/broodmother/patch/Initialize(mapload, new_spawner)
|
||||
. = ..()
|
||||
INVOKE_ASYNC(src, .proc/createpatch)
|
||||
|
||||
/obj/effect/temp_visual/goliath_tentacle/broodmother/patch/proc/createpatch()
|
||||
var/tentacle_locs = spiral_range_turfs(1, get_turf(src))
|
||||
for(var/T in tentacle_locs)
|
||||
new /obj/effect/temp_visual/goliath_tentacle/broodmother(T, spawner)
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
butcher_results = list(/obj/item/reagent_containers/food/snacks/nugget = 5)
|
||||
|
||||
/mob/living/simple_animal/hostile/retaliate/frog/Crossed(AM as mob|obj)
|
||||
. = ..()
|
||||
if(!stat && isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
if(L.mob_size > MOB_SIZE_TINY)
|
||||
playsound(src, stepped_sound, 50, 1)
|
||||
playsound(src, stepped_sound, 50, TRUE)
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
|
||||
//Headset for Poly to yell at engineers :)
|
||||
var/obj/item/radio/headset/ears = null
|
||||
/// spawns with headset
|
||||
var/spawns_with_headset = FALSE
|
||||
|
||||
//The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from,
|
||||
//mobs it wants to attack or mobs that have attacked it
|
||||
@@ -105,13 +107,14 @@
|
||||
|
||||
/mob/living/simple_animal/parrot/Initialize()
|
||||
. = ..()
|
||||
if(!ears)
|
||||
var/headset = pick(/obj/item/radio/headset/headset_sec, \
|
||||
/obj/item/radio/headset/headset_eng, \
|
||||
/obj/item/radio/headset/headset_med, \
|
||||
/obj/item/radio/headset/headset_sci, \
|
||||
/obj/item/radio/headset/headset_cargo)
|
||||
ears = new headset(src)
|
||||
if(spawns_with_headset)
|
||||
if(!ears)
|
||||
var/headset = pick(/obj/item/radio/headset/headset_sec, \
|
||||
/obj/item/radio/headset/headset_eng, \
|
||||
/obj/item/radio/headset/headset_med, \
|
||||
/obj/item/radio/headset/headset_sci, \
|
||||
/obj/item/radio/headset/headset_cargo)
|
||||
ears = new headset(src)
|
||||
|
||||
parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var
|
||||
|
||||
@@ -881,6 +884,7 @@
|
||||
speak = list("Poly wanna cracker!", ":e Check the crystal, you chucklefucks!",":e Wire the solars, you lazy bums!",":e WHO TOOK THE DAMN HARDSUITS?",":e OH GOD ITS ABOUT TO DELAMINATE CALL THE SHUTTLE")
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
speak_chance = 3
|
||||
spawns_with_headset = TRUE
|
||||
var/memory_saved = FALSE
|
||||
var/rounds_survived = 0
|
||||
var/longest_survival = 0
|
||||
@@ -1189,4 +1193,4 @@
|
||||
icon_state = "mtoo-flap"
|
||||
icon_living = "mtoo-flap"
|
||||
icon_dead = "mtoo-dead"
|
||||
icon_sit = "mtoo_sit"
|
||||
icon_sit = "mtoo_sit"
|
||||
|
||||
@@ -177,3 +177,6 @@
|
||||
var/list/ability_actions
|
||||
/// ability = list(data). see __DEFINES/mobs/innate_abilities.dm
|
||||
var/list/ability_properties
|
||||
|
||||
///Override for sound_environments. If this is set the user will always hear a specific type of reverb (Instead of the area defined reverb)
|
||||
var/sound_environment_override = SOUND_ENVIRONMENT_NONE
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
return FALSE
|
||||
//We are now going to move
|
||||
var/add_delay = mob.movement_delay()
|
||||
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direction) && EWCOMPONENT(direction)) ? 2 : 1 ) ), FALSE) // set it now in case of pulled objects
|
||||
if(old_move_delay + (add_delay*MOVEMENT_DELAY_BUFFER_DELTA) + MOVEMENT_DELAY_BUFFER > world.time)
|
||||
move_delay = old_move_delay
|
||||
else
|
||||
@@ -95,6 +96,7 @@
|
||||
|
||||
if((direction & (direction - 1)) && mob.loc == n) //moved diagonally successfully
|
||||
add_delay *= 2
|
||||
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay), FALSE)
|
||||
move_delay += add_delay
|
||||
if(.) // If mob is null here, we deserve the runtime
|
||||
if(mob.throwing)
|
||||
@@ -104,6 +106,8 @@
|
||||
if(AM && AM.density && !SEND_SIGNAL(L, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) && !ismob(AM))
|
||||
L.setDir(turn(L.dir, 180))
|
||||
|
||||
last_move = world.time
|
||||
|
||||
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVE, src, direction, n, oldloc, add_delay)
|
||||
|
||||
/// Process_Grab(): checks for grab, attempts to break if so. Return TRUE to prevent movement.
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
// user?.mind?.adjust_experience(/datum/skill/gaming, 1)
|
||||
if(boss_hp <= 0)
|
||||
heads_up = "You have crushed [boss_name]! Rejoice!"
|
||||
playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3)
|
||||
game_active = FALSE
|
||||
program_icon_state = "arcade_off"
|
||||
if(istype(computer))
|
||||
@@ -37,7 +37,7 @@
|
||||
sleep(10)
|
||||
else if(player_hp <= 0 || player_mp <= 0)
|
||||
heads_up = "You have been defeated... how will the station survive?"
|
||||
playsound(computer.loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3)
|
||||
game_active = FALSE
|
||||
program_icon_state = "arcade_off"
|
||||
if(istype(computer))
|
||||
@@ -57,17 +57,17 @@
|
||||
return
|
||||
if (boss_mp <= 5)
|
||||
heads_up = "[boss_mpamt] magic power has been stolen from you!"
|
||||
playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3)
|
||||
player_mp -= boss_mpamt
|
||||
boss_mp += boss_mpamt
|
||||
else if(boss_mp > 5 && boss_hp <12)
|
||||
heads_up = "[boss_name] heals for [bossheal] health!"
|
||||
playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
|
||||
boss_hp += bossheal
|
||||
boss_mp -= boss_mpamt
|
||||
else
|
||||
heads_up = "[boss_name] attacks you for [boss_attackamt] damage!"
|
||||
playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
|
||||
player_hp -= boss_attackamt
|
||||
|
||||
pause_state = FALSE
|
||||
@@ -106,7 +106,7 @@
|
||||
attackamt = rand(2,6)// + rand(0, gamerSkill)
|
||||
pause_state = TRUE
|
||||
heads_up = "You attack for [attackamt] damage."
|
||||
playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3)
|
||||
boss_hp -= attackamt
|
||||
sleep(10)
|
||||
game_check()
|
||||
@@ -123,7 +123,7 @@
|
||||
healcost = rand(1, maxPointCost)
|
||||
pause_state = TRUE
|
||||
heads_up = "You heal for [healamt] damage."
|
||||
playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3)
|
||||
player_hp += healamt
|
||||
player_mp -= healcost
|
||||
sleep(10)
|
||||
@@ -136,7 +136,7 @@
|
||||
rechargeamt = rand(4,7)// + rand(0, gamerSkill)
|
||||
pause_state = TRUE
|
||||
heads_up = "You regain [rechargeamt] magic power."
|
||||
playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3, falloff = 10)
|
||||
playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3)
|
||||
player_mp += rechargeamt
|
||||
sleep(10)
|
||||
game_check()
|
||||
|
||||
@@ -96,7 +96,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
|
||||
return TRUE
|
||||
remove_movespeed_modifier(existing, FALSE)
|
||||
if(length(movespeed_modification))
|
||||
BINARY_INSERT(type_or_datum.id, movespeed_modification, datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE)
|
||||
BINARY_INSERT(type_or_datum.id, movespeed_modification, /datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE)
|
||||
LAZYSET(movespeed_modification, type_or_datum.id, type_or_datum)
|
||||
if(update)
|
||||
update_movespeed()
|
||||
@@ -217,13 +217,25 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
|
||||
else
|
||||
continue
|
||||
. = M.apply_multiplicative(., src)
|
||||
var/old = cached_multiplicative_slowdown // CITAEDL EDIT - To make things a bit less jarring, when in situations where
|
||||
// your delay decreases, "give" the delay back to the client
|
||||
cached_multiplicative_slowdown = .
|
||||
var/diff = old - cached_multiplicative_slowdown
|
||||
if((diff > 0) && client)
|
||||
if(!client)
|
||||
return
|
||||
var/diff = (client.last_move - client.move_delay) - cached_multiplicative_slowdown
|
||||
if(diff > 0)
|
||||
if(client.move_delay > world.time + 1.5)
|
||||
client.move_delay -= diff
|
||||
var/timeleft = world.time - client.move_delay
|
||||
var/elapsed = world.time - client.last_move
|
||||
var/glide_size_current = glide_size
|
||||
if((timeleft <= 0) || (elapsed > 20))
|
||||
set_glide_size(16, TRUE)
|
||||
return
|
||||
var/pixels_moved = glide_size_current * elapsed * (1 / world.tick_lag)
|
||||
// calculate glidesize needed to move to the next tile within timeleft deciseconds
|
||||
var/ticks_allowed = timeleft / world.tick_lag
|
||||
var/pixels_per_tick = pixels_moved / ticks_allowed
|
||||
set_glide_size(pixels_per_tick * GLOB.glide_size_multiplier, TRUE)
|
||||
|
||||
/// Get the move speed modifiers list of the mob
|
||||
/mob/proc/get_movespeed_modifiers()
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
var/mod = CONFIG_GET(number/movedelay/walk_delay)
|
||||
multiplicative_slowdown = isnum(mod)? mod : initial(multiplicative_slowdown)
|
||||
|
||||
/datum/movespeed_modifier/config_wak_run/walk/apply_multiplicative(existing, mob/target)
|
||||
. = ..()
|
||||
if(HAS_TRAIT(target, TRAIT_SPEEDY_STEP))
|
||||
. -= 1.25
|
||||
|
||||
/datum/movespeed_modifier/config_walk_run/run/sync()
|
||||
var/mod = CONFIG_GET(number/movedelay/run_delay)
|
||||
multiplicative_slowdown = isnum(mod)? mod : initial(multiplicative_slowdown)
|
||||
|
||||
@@ -56,12 +56,14 @@
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/machinery/field/containment/Crossed(mob/mover)
|
||||
if(isliving(mover))
|
||||
shock(mover)
|
||||
/obj/machinery/field/containment/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(isliving(AM))
|
||||
shock(AM)
|
||||
|
||||
if(ismachinery(AM) || isstructure(AM) || ismecha(AM))
|
||||
bump_field(AM)
|
||||
|
||||
if(ismachinery(mover) || isstructure(mover) || ismecha(mover))
|
||||
bump_field(mover)
|
||||
|
||||
/obj/machinery/field/containment/proc/set_master(master1,master2)
|
||||
if(!master1 || !master2)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
movement_range = 0
|
||||
|
||||
/obj/effect/accelerated_particle/Crossed(atom/A)
|
||||
. = ..()
|
||||
if(isliving(A))
|
||||
toxmob(A)
|
||||
|
||||
|
||||
@@ -348,11 +348,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
/obj/machinery/power/supermatter_crystal/proc/alarm()
|
||||
switch(get_status())
|
||||
if(SUPERMATTER_DELAMINATING)
|
||||
playsound(src, 'sound/misc/bloblarm.ogg', 100)
|
||||
playsound(src, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10)
|
||||
if(SUPERMATTER_EMERGENCY)
|
||||
playsound(src, 'sound/machines/engine_alert1.ogg', 100)
|
||||
playsound(src, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
|
||||
if(SUPERMATTER_DANGER)
|
||||
playsound(src, 'sound/machines/engine_alert2.ogg', 100)
|
||||
playsound(src, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
|
||||
if(SUPERMATTER_WARNING)
|
||||
playsound(src, 'sound/machines/terminal_alert.ogg', 75)
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
item_flags = NEEDS_PERMIT
|
||||
attack_verb = list("struck", "hit", "bashed")
|
||||
attack_speed = CLICK_CD_RANGE
|
||||
var/ranged_attack_speed = CLICK_CD_RANGE
|
||||
var/melee_attack_speed = CLICK_CD_MELEE
|
||||
|
||||
var/fire_sound = "gunshot"
|
||||
var/suppressed = null //whether or not a message is displayed when fired
|
||||
@@ -158,7 +160,7 @@
|
||||
user.UseStaminaBuffer(safe_cost)
|
||||
|
||||
if(suppressed)
|
||||
playsound(user, fire_sound, 10, 1)
|
||||
playsound(user, fire_sound, 10, TRUE, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
|
||||
else
|
||||
playsound(user, fire_sound, 50, 1)
|
||||
if(message)
|
||||
@@ -173,12 +175,25 @@
|
||||
for(var/obj/O in contents)
|
||||
O.emp_act(severity)
|
||||
|
||||
/obj/item/gun/attack(mob/living/M, mob/user)
|
||||
. = ..()
|
||||
if(!(. & DISCARD_LAST_ACTION))
|
||||
user.DelayNextAction(melee_attack_speed)
|
||||
|
||||
/obj/item/gun/attack_obj(obj/O, mob/user)
|
||||
. = ..()
|
||||
if(!(. & DISCARD_LAST_ACTION))
|
||||
user.DelayNextAction(melee_attack_speed)
|
||||
|
||||
/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params)
|
||||
. = ..()
|
||||
if(!CheckAttackCooldown(user, target))
|
||||
if(!CheckAttackCooldown(user, target, TRUE))
|
||||
return
|
||||
process_afterattack(target, user, flag, params)
|
||||
|
||||
/obj/item/gun/CheckAttackCooldown(mob/user, atom/target, shooting = FALSE)
|
||||
return user.CheckActionCooldown(shooting? ranged_attack_speed : attack_speed, clickdelay_from_next_action, clickdelay_mod_bypass, clickdelay_ignores_next_action)
|
||||
|
||||
/obj/item/gun/proc/process_afterattack(atom/target, mob/living/user, flag, params)
|
||||
if(!target)
|
||||
return
|
||||
|
||||
@@ -471,11 +471,10 @@
|
||||
if(paused || !isturf(loc))
|
||||
return
|
||||
|
||||
var/ds = (SSprojectiles.flags & SS_TICKER)? (wait * world.tick_lag) : wait
|
||||
var/required_pixels = (pixels_per_second * ds * 0.1) + pixels_tick_leftover
|
||||
var/required_pixels = (pixels_per_second * wait) + pixels_tick_leftover
|
||||
if(required_pixels >= pixel_increment_amount)
|
||||
pixels_tick_leftover = MODULUS(required_pixels, pixel_increment_amount)
|
||||
pixel_move(FLOOR(required_pixels / pixel_increment_amount, 1), FALSE, ds, SSprojectiles.global_projectile_speed_multiplier)
|
||||
pixel_move(FLOOR(required_pixels / pixel_increment_amount, 1), FALSE, wait, SSprojectiles.global_projectile_speed_multiplier)
|
||||
else
|
||||
pixels_tick_leftover = required_pixels
|
||||
|
||||
@@ -603,7 +602,7 @@
|
||||
* Trajectory multiplier directly modifies the factor of pixel_increment_amount to go per time.
|
||||
* It's complicated, so probably just don't mess with this unless you know what you're doing.
|
||||
*/
|
||||
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, deciseconds_equivalent = world.tick_lag, trajectory_multiplier = 1, allow_animation = TRUE)
|
||||
/obj/item/projectile/proc/pixel_move(times, hitscanning = FALSE, seconds_equivalent = world.tick_lag * 0.1, trajectory_multiplier = 1, allow_animation = TRUE)
|
||||
if(!loc || !trajectory)
|
||||
return
|
||||
if(!nondirectional_sprite && !hitscanning)
|
||||
@@ -620,7 +619,7 @@
|
||||
if(homing_target)
|
||||
// No datum/points, too expensive.
|
||||
var/angle = closer_angle_difference(Angle, get_projectile_angle(src, homing_target))
|
||||
var/max_turn = homing_turn_speed * deciseconds_equivalent * 0.1
|
||||
var/max_turn = homing_turn_speed * seconds_equivalent
|
||||
setAngle(Angle + clamp(angle, -max_turn, max_turn))
|
||||
// HOMING END
|
||||
trajectory.increment(trajectory_multiplier)
|
||||
|
||||
@@ -2288,6 +2288,7 @@ All effects don't start immediately, but rather get worse over time; the rate is
|
||||
//Race-Base-Drinks//
|
||||
////////////////////
|
||||
/datum/reagent/consumable/ethanol/species_drink
|
||||
name = "Species Drink"
|
||||
var/species_required
|
||||
var/disgust = 26
|
||||
boozepwr = 50
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
var/obj/pipe_type = /obj/structure/disposalpipe/segment
|
||||
var/pipename
|
||||
|
||||
/obj/structure/disposalconstruct/Initialize(loc, _pipe_type, _dir = SOUTH, flip = FALSE, obj/make_from)
|
||||
/obj/structure/disposalconstruct/Initialize(mapload, _pipe_type, _dir = SOUTH, flip = FALSE, obj/make_from)
|
||||
. = ..()
|
||||
if(make_from)
|
||||
pipe_type = make_from.type
|
||||
setDir(make_from.dir)
|
||||
anchored = TRUE
|
||||
set_anchored(TRUE)
|
||||
|
||||
else
|
||||
if(_pipe_type)
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
update_icon()
|
||||
|
||||
// AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE)
|
||||
|
||||
/obj/structure/disposalconstruct/Move()
|
||||
var/old_dir = dir
|
||||
..()
|
||||
|
||||
@@ -73,14 +73,16 @@
|
||||
/obj/structure/disposalholder/proc/move()
|
||||
set waitfor = FALSE
|
||||
var/obj/structure/disposalpipe/last
|
||||
var/ticks = 1
|
||||
while(active)
|
||||
var/obj/structure/disposalpipe/curr = loc
|
||||
last = curr
|
||||
set_glide_size(DELAY_TO_GLIDE_SIZE(ticks * world.tick_lag))
|
||||
curr = curr.transfer(src)
|
||||
if(!curr && active)
|
||||
last.expel(src, loc, dir)
|
||||
|
||||
stoplag()
|
||||
ticks = stoplag()
|
||||
if(!(count--))
|
||||
active = FALSE
|
||||
|
||||
|
||||
@@ -111,4 +111,4 @@
|
||||
id = "spaceship_navigation_beacon"
|
||||
build_path = /obj/item/circuitboard/machine/spaceship_navigation_beacon
|
||||
category = list ("Teleportation Machinery")
|
||||
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE
|
||||
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_SCIENCE
|
||||
|
||||
@@ -274,6 +274,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
|
||||
return
|
||||
|
||||
/obj/structure/stone_tile/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(falling || fallen)
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
@@ -21,6 +21,9 @@ GLOBAL_VAR_INIT(hhmysteryRoomNumber, 1337)
|
||||
/obj/item/hilbertshotel/Initialize()
|
||||
. = ..()
|
||||
//Load templates
|
||||
INVOKE_ASYNC(src, .proc/prepare_rooms)
|
||||
|
||||
/obj/item/hilbertshotel/proc/prepare_rooms()
|
||||
hotelRoomTemp = new()
|
||||
hotelRoomTempEmpty = new()
|
||||
hotelRoomTempLore = new()
|
||||
|
||||
@@ -807,7 +807,7 @@
|
||||
for(var/mob/M in SSmobs.clients_by_zlevel[z])
|
||||
var/dist_far = get_dist(M, distant_source)
|
||||
if(dist_far <= long_range && dist_far > range)
|
||||
M.playsound_local(distant_source, "sound/effects/[selected_sound]_distance.ogg", 100, falloff = 20)
|
||||
M.playsound_local(distant_source, "sound/effects/[selected_sound]_distance.ogg", 100)
|
||||
else if(dist_far <= range)
|
||||
var/source
|
||||
if(engine_list.len == 0)
|
||||
@@ -819,7 +819,7 @@
|
||||
if(dist_near < closest_dist)
|
||||
source = O
|
||||
closest_dist = dist_near
|
||||
M.playsound_local(source, "sound/effects/[selected_sound].ogg", 100, falloff = range / 2)
|
||||
M.playsound_local(source, "sound/effects/[selected_sound].ogg", 100)
|
||||
|
||||
// Losing all initial engines should get you 2
|
||||
// Adding another set of engines at 0.5 time
|
||||
|
||||
@@ -60,4 +60,4 @@ obj/machinery/spaceship_navigation_beacon/emp_act()
|
||||
if(default_deconstruction_crowbar(W))
|
||||
return
|
||||
|
||||
return ..()
|
||||
return ..()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/obj/effect/proc_holder/spell/spacetime_dist
|
||||
name = "Spacetime Distortion"
|
||||
desc = "Entangle the strings of spacetime to deny easy movement around you. The strings vibrate..."
|
||||
desc = "Entangle the strings of space-time in an area around you, randomizing the layout and making proper movement impossible. The strings vibrate..."
|
||||
charge_max = 300
|
||||
var/duration = 150
|
||||
range = 7
|
||||
@@ -10,8 +10,9 @@
|
||||
sound = 'sound/effects/magic.ogg'
|
||||
cooldown_min = 300
|
||||
level_max = 0
|
||||
// action_icon_state = "spacetime"
|
||||
|
||||
/obj/effect/proc_holder/spell/spacetime_dist/can_cast(mob/user = usr, skipcharge = FALSE, silent = FALSE)
|
||||
/obj/effect/proc_holder/spell/spacetime_dist/can_cast(mob/user = usr)
|
||||
if(ready)
|
||||
return ..()
|
||||
return FALSE
|
||||
@@ -97,10 +98,11 @@
|
||||
busy = TRUE
|
||||
flick("purplesparkles", src)
|
||||
AM.forceMove(get_turf(src))
|
||||
playsound(get_turf(src),sound,70,0)
|
||||
playsound(get_turf(src),sound,70,FALSE)
|
||||
busy = FALSE
|
||||
|
||||
/obj/effect/cross_action/spacetime_dist/Crossed(atom/movable/AM)
|
||||
. = ..()
|
||||
if(!busy)
|
||||
walk_link(AM)
|
||||
|
||||
@@ -110,7 +112,8 @@
|
||||
else
|
||||
walk_link(user)
|
||||
|
||||
/obj/effect/cross_action/spacetime_dist/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/effect/cross_action/spacetime_dist/on_attack_hand(mob/user)
|
||||
walk_link(user)
|
||||
|
||||
/obj/effect/cross_action/spacetime_dist/attack_paw(mob/user)
|
||||
|
||||
@@ -1319,7 +1319,7 @@
|
||||
if(E.phase > 1)
|
||||
if(user.ckey == E.enthrallID && user.real_name == E.master.real_name)
|
||||
E.master = user
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, H, "<span class='nicegreen'>[(E.lewd?"You hear the words of your [E.enthrallGender] again!! They're back!!":"You recognise the voice of [E.master].")]</b></span>"), 5)
|
||||
to_chat(H, "<span class='nicegreen'>[(E.lewd?"You hear the words of your [E.enthrallGender] again!! They're back!!":"You recognise the voice of [E.master].")]</b></span>")
|
||||
to_chat(user, "<span class='notice'><i>[H] looks at you with sparkling eyes, recognising you!</i></span>")
|
||||
|
||||
//I dunno how to do state objectives without them revealing they're an antag
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Unit Tests
|
||||
|
||||
## What is unit testing?
|
||||
|
||||
Unit tests are automated code to verify that parts of the game work exactly as they should. For example, [a test to make sure that the amputation surgery actually amputates the limb](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/surgeries.dm#L1-L13). These are ran every time a PR is made, and thus are very helpful for preventing bugs from cropping up in your code that would've otherwise gone unnoticed. For example, would you have thought to check [that beach boys would still work the same after editing pizza](https://github.com/tgstation/tgstation/pull/53641#issuecomment-691384934)? If you value your time, probably not.
|
||||
|
||||
On their most basic level, when `UNIT_TESTS` is defined, all subtypes of `/datum/unit_test` will have their `Run` proc executed. From here, if `Fail` is called at any point, then the tests will report as failed.
|
||||
|
||||
## How do I write one?
|
||||
1. Find a relevant file.
|
||||
|
||||
All unit test related code is in `code/modules/unit_tests`. If you are adding a new test for a surgery, for example, then you'd open `surgeries.dm`. If a relevant file does not exist, simply create one in this folder, then `#include` it in `_unit_tests.dm`.
|
||||
|
||||
2. Create the unit test.
|
||||
|
||||
To make a new unit test, you simply need to define a `/datum/unit_test`.
|
||||
|
||||
For example, let's suppose that we are creating a test to make sure a proc `square` correctly raises inputs to the power of two. We'd start with first:
|
||||
|
||||
```
|
||||
/datum/unit_test/square/Run()
|
||||
```
|
||||
|
||||
This defines our new unit test, `/datum/unit_test/square`. Inside this function, we're then going to run through whatever we want to check. Tests provide a few assertion functions to make this easy. For now, we're going to use `TEST_ASSERT_EQUAL`.
|
||||
|
||||
```
|
||||
/datum/unit_test/square/Run()
|
||||
TEST_ASSERT_EQUAL(square(3), 9, "square(3) did not return 9")
|
||||
TEST_ASSERT_EQUAL(square(4), 16, "square(4) did not return 16")
|
||||
```
|
||||
|
||||
As you can hopefully tell, we're simply checking if the output of `square` matches the output we are expecting. If the test fails, it'll report the error message given as well as whatever the actual output was.
|
||||
|
||||
3. Run the unit test
|
||||
|
||||
Open `code/_compile_options.dm` and uncomment the following line.
|
||||
|
||||
```
|
||||
//#define UNIT_TESTS //If this is uncommented, we do a single run though of the game setup and tear down process with unit tests in between
|
||||
```
|
||||
|
||||
Then, run tgstation.dmb in Dream Daemon. Don't bother trying to connect, you won't need to. You'll be able to see the outputs of all the tests. You'll get to see which tests failed and for what reason. If they all pass, you're set!
|
||||
|
||||
## How to think about tests
|
||||
|
||||
Unit tests exist to prevent bugs that would happen in a real game. Thus, they should attempt to emulate the game world wherever possible. For example, the [quick swap sanity test](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/quick_swap_sanity.dm) emulates a *real* scenario of the bug it fixed occurring by creating a character and giving it real items. The unrecommended alternative would be to create special test-only items. This isn't a hard rule, the [reagent method exposure tests](https://github.com/tgstation/tgstation/blob/e416283f162b86345a8623125ab866839b1ac40d/code/modules/unit_tests/reagent_mod_expose.dm) create a test-only reagent for example, but do keep it in mind.
|
||||
|
||||
Unit tests should also be just that--testing *units* of code. For example, instead of having one massive test for reagents, there are instead several smaller tests for testing exposure, metabolization, etc.
|
||||
|
||||
## The unit testing API
|
||||
|
||||
You can find more information about all of these from their respective doc comments, but for a brief overview:
|
||||
|
||||
`/datum/unit_test` - The base for all tests to be ran. Subtypes must override `Run()`. `New()` and `Destroy()` can be used for setup and teardown. To fail, use `Fail(reason)`.
|
||||
|
||||
`/datum/unit_test/proc/allocate(type, ...)` - Allocates an instance of the provided type with the given arguments. Is automatically destroyed when the test is over. Commonly seen in the form of `var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)`.
|
||||
|
||||
`TEST_ASSERT(assertion, reason)` - Stops the unit test and fails if the assertion is not met. For example: `TEST_ASSERT(powered(), "Machine is not powered")`.
|
||||
|
||||
`TEST_ASSERT_EQUAL(a, b, message)` - Same as `TEST_ASSERT`, but checks if `a == b`. If not, gives a helpful message showing what both `a` and `b` were. For example: `TEST_ASSERT_EQUAL(2 + 2, 4, "The universe is falling apart before our eyes!")`.
|
||||
|
||||
`TEST_ASSERT_NOTEQUAL(a, b, message)` - Same as `TEST_ASSERT_EQUAL`, but reversed.
|
||||
|
||||
`TEST_FOCUS(test_path)` - *Only* run the test provided within the parameters. Useful for reducing noise. For example, if we only want to run our example square test, we can add `TEST_FOCUS(/datum/unit_test/square)`. Should *never* be pushed in a pull request--you will be laughed at.
|
||||
|
||||
## Final Notes
|
||||
|
||||
- Writing tests before you attempt to fix the bug can actually speed up development a lot! It means you don't have to go in game and folllow the same exact steps manually every time. This process is known as "TDD" (test driven development). Write the test first, make sure it fails, *then* start work on the fix/feature, and you'll know you're done when your tests pass. If you do try this, do make sure to confirm in a non-testing environment just to double check.
|
||||
- Make sure that your tests don't accidentally call RNG functions like `prob`. Since RNG is seeded during tests, you may not realize you have until someone else makes a PR and the tests fail!
|
||||
- Do your best not to change the behavior of non-testing code during tests. While it may sometimes be necessary in the case of situations such as the above, it is still a slippery slope that can lead to the code you're testing being too different from the production environment to be useful.
|
||||
@@ -1,42 +1,80 @@
|
||||
//include unit test files in this module in this ifdef
|
||||
//Keep this sorted alphabetically
|
||||
|
||||
#ifdef UNIT_TESTS
|
||||
#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
|
||||
|
||||
/// Asserts that a condition is true
|
||||
/// If the condition is not true, fails the test
|
||||
#define TEST_ASSERT(assertion, reason) if (!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]") }
|
||||
|
||||
/// Asserts that the two parameters passed are equal, fails otherwise
|
||||
/// Optionally allows an additional message in the case of a failure
|
||||
#define TEST_ASSERT_EQUAL(a, b, message) if ((a) != (b)) { return Fail("Expected [isnull(a) ? "null" : a] to be equal to [isnull(b) ? "null" : b].[message ? " [message]" : ""]") }
|
||||
#define TEST_ASSERT_EQUAL(a, b, message) do { \
|
||||
var/lhs = ##a; \
|
||||
var/rhs = ##b; \
|
||||
if (lhs != rhs) { \
|
||||
return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \
|
||||
} \
|
||||
} while (FALSE)
|
||||
|
||||
/// Asserts that the two parameters passed are not equal, fails otherwise
|
||||
/// Optionally allows an additional message in the case of a failure
|
||||
#define TEST_ASSERT_NOTEQUAL(a, b, message) do { \
|
||||
var/lhs = ##a; \
|
||||
var/rhs = ##b; \
|
||||
if (lhs == rhs) { \
|
||||
return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]"); \
|
||||
} \
|
||||
} while (FALSE)
|
||||
|
||||
/// *Only* run the test provided within the parentheses
|
||||
/// This is useful for debugging when you want to reduce noise, but should never be pushed
|
||||
/// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)`
|
||||
#define TEST_FOCUS(test_path) ##test_path { focus = TRUE; }
|
||||
|
||||
#include "anchored_mobs.dm"
|
||||
#include "bespoke_id.dm"
|
||||
// #include "binary_insert.dm"
|
||||
// #include "card_mismatch.dm" shame we don't have this!
|
||||
#include "binary_insert.dm"
|
||||
// #include "card_mismatch.dm"
|
||||
#include "chain_pull_through_space.dm"
|
||||
#include "character_saving.dm"
|
||||
// #include "combat.dm"
|
||||
#include "component_tests.dm"
|
||||
// #include "confusion.dm"
|
||||
// #include "keybinding_init.dm"
|
||||
// #include "emoting.dm"
|
||||
// #include "heretic_knowledge.dm"
|
||||
// #include "holidays.dm"
|
||||
#include "initialize_sanity.dm"
|
||||
#include "keybinding_init.dm"
|
||||
#include "machine_disassembly.dm"
|
||||
#include "medical_wounds.dm"
|
||||
#include "merge_type.dm"
|
||||
// #include "metabolizing.dm"
|
||||
// #include "outfit_sanity.dm"
|
||||
// #include "pills.dm"
|
||||
// #include "plantgrowth_tests.dm"
|
||||
// #include "quick_swap_sanity.dm" - we don't have quick swap yet
|
||||
// #include "projectiles.dm"
|
||||
#include "reagent_id_typos.dm"
|
||||
// #include "reagent_mod_expose.dm"
|
||||
// #include "reagent_mod_procs.dm"
|
||||
#include "reagent_recipe_collisions.dm"
|
||||
#include "resist.dm"
|
||||
// #include "say.dm" //no saymods, someone update saycode please.
|
||||
// #include "say.dm"
|
||||
// #include "serving_tray.dm"
|
||||
// #include "siunit.dm"
|
||||
#include "spawn_humans.dm"
|
||||
// #include "species_whitelists.dm"
|
||||
// #include "stomach.dm"
|
||||
#include "subsystem_init.dm"
|
||||
// #include "surgeries.dm" // fails at random due to a race condition, commented out for now
|
||||
#include "surgeries.dm"
|
||||
#include "teleporters.dm"
|
||||
#include "timer_sanity.dm"
|
||||
#include "unit_test.dm"
|
||||
|
||||
/// CIT TESTS
|
||||
#include "character_saving.dm"
|
||||
|
||||
#undef TEST_ASSERT
|
||||
#undef TEST_ASSERT_EQUAL
|
||||
#undef TEST_ASSERT_NOTEQUAL
|
||||
#undef TEST_FOCUS
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/datum/unit_test/card_mismatch
|
||||
|
||||
/datum/unit_test/card_mismatch/Run()
|
||||
var/message = checkCardpacks(SStrading_card_game.card_packs)
|
||||
message += checkCardDatums()
|
||||
if(message)
|
||||
Fail(message)
|
||||
@@ -0,0 +1,98 @@
|
||||
/datum/unit_test/harm_punch/Run()
|
||||
var/mob/living/carbon/human/puncher = allocate(/mob/living/carbon/human)
|
||||
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
|
||||
|
||||
// Avoid all randomness in tests
|
||||
ADD_TRAIT(puncher, TRAIT_PERFECT_ATTACKER, INNATE_TRAIT)
|
||||
|
||||
puncher.a_intent_change(INTENT_HARM)
|
||||
victim.attack_hand(puncher)
|
||||
|
||||
TEST_ASSERT(victim.getBruteLoss() > 0, "Victim took no brute damage after being punched")
|
||||
|
||||
/datum/unit_test/harm_melee/Run()
|
||||
var/mob/living/carbon/human/tider = allocate(/mob/living/carbon/human)
|
||||
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox)
|
||||
|
||||
tider.put_in_active_hand(toolbox, forced = TRUE)
|
||||
tider.a_intent_change(INTENT_HARM)
|
||||
victim.attackby(toolbox, tider)
|
||||
|
||||
TEST_ASSERT(victim.getBruteLoss() > 0, "Victim took no brute damage after being hit by a toolbox")
|
||||
|
||||
/datum/unit_test/harm_different_damage/Run()
|
||||
var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human)
|
||||
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/weldingtool/welding_tool = allocate(/obj/item/weldingtool)
|
||||
|
||||
attacker.put_in_active_hand(welding_tool, forced = TRUE)
|
||||
attacker.a_intent_change(INTENT_HARM)
|
||||
welding_tool.attack_self(attacker) // Turn it on
|
||||
victim.attackby(welding_tool, attacker)
|
||||
|
||||
TEST_ASSERT_EQUAL(victim.getBruteLoss(), 0, "Victim took brute damage from a lit welding tool")
|
||||
TEST_ASSERT(victim.getFireLoss() > 0, "Victim took no burn damage after being hit by a lit welding tool")
|
||||
|
||||
/datum/unit_test/attack_chain
|
||||
var/attack_hit
|
||||
var/post_attack_hit
|
||||
var/pre_attack_hit
|
||||
|
||||
/datum/unit_test/attack_chain/proc/attack_hit()
|
||||
attack_hit = TRUE
|
||||
|
||||
/datum/unit_test/attack_chain/proc/post_attack_hit()
|
||||
post_attack_hit = TRUE
|
||||
|
||||
/datum/unit_test/attack_chain/proc/pre_attack_hit()
|
||||
pre_attack_hit = TRUE
|
||||
|
||||
/datum/unit_test/attack_chain/Run()
|
||||
var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human)
|
||||
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox)
|
||||
|
||||
RegisterSignal(toolbox, COMSIG_ITEM_PRE_ATTACK, .proc/pre_attack_hit)
|
||||
RegisterSignal(toolbox, COMSIG_ITEM_ATTACK, .proc/attack_hit)
|
||||
RegisterSignal(toolbox, COMSIG_ITEM_AFTERATTACK, .proc/post_attack_hit)
|
||||
|
||||
attacker.put_in_active_hand(toolbox, forced = TRUE)
|
||||
attacker.a_intent_change(INTENT_HARM)
|
||||
toolbox.melee_attack_chain(attacker, victim)
|
||||
|
||||
TEST_ASSERT(pre_attack_hit, "Pre-attack signal was not fired")
|
||||
TEST_ASSERT(attack_hit, "Attack signal was not fired")
|
||||
TEST_ASSERT(post_attack_hit, "Post-attack signal was not fired")
|
||||
|
||||
/datum/unit_test/disarm/Run()
|
||||
var/mob/living/carbon/human/attacker = allocate(/mob/living/carbon/human)
|
||||
var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/storage/toolbox/toolbox = allocate(/obj/item/storage/toolbox)
|
||||
|
||||
victim.put_in_active_hand(toolbox, forced = TRUE)
|
||||
attacker.a_intent_change(INTENT_DISARM)
|
||||
|
||||
var/obj/structure/barricade/dense_object = allocate(/obj/structure/barricade)
|
||||
|
||||
// Attacker --> Victim --> Empty space --> Wall
|
||||
attacker.forceMove(run_loc_bottom_left)
|
||||
victim.forceMove(locate(run_loc_bottom_left.x + 1, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
dense_object.forceMove(locate(run_loc_bottom_left.x + 3, run_loc_bottom_left.y, run_loc_bottom_left.z))
|
||||
|
||||
// First disarm, world should now look like:
|
||||
// Attacker --> Empty space --> Victim --> Wall
|
||||
victim.attack_hand(attacker)
|
||||
|
||||
TEST_ASSERT_EQUAL(victim.loc.x, run_loc_bottom_left.x + 2, "Victim wasn't moved back after being pushed")
|
||||
TEST_ASSERT(!victim.has_status_effect(STATUS_EFFECT_KNOCKDOWN), "Victim was knocked down despite not being against a wall")
|
||||
TEST_ASSERT_EQUAL(victim.get_active_held_item(), toolbox, "Victim dropped toolbox despite not being against a wall")
|
||||
|
||||
attacker.forceMove(get_step(attacker, EAST))
|
||||
|
||||
// Second disarm, victim was against wall and should be down
|
||||
victim.attack_hand(attacker)
|
||||
|
||||
TEST_ASSERT_EQUAL(victim.loc.x, run_loc_bottom_left.x + 2, "Victim was moved after being pushed against a wall")
|
||||
TEST_ASSERT(victim.has_status_effect(STATUS_EFFECT_KNOCKDOWN), "Victim was not knocked down after being pushed against a wall")
|
||||
TEST_ASSERT_EQUAL(victim.get_active_held_item(), null, "Victim didn't drop toolbox after being pushed against a wall")
|
||||
@@ -0,0 +1,16 @@
|
||||
// Checks that the confusion symptom correctly gives, and removes, confusion
|
||||
/datum/unit_test/confusion_symptom/Run()
|
||||
var/mob/living/carbon/human/H = allocate(/mob/living/carbon/human)
|
||||
var/datum/disease/advance/confusion/disease = allocate(/datum/disease/advance/confusion)
|
||||
var/datum/symptom/confusion/confusion = disease.symptoms[1]
|
||||
disease.processing = TRUE
|
||||
disease.update_stage(5)
|
||||
disease.infect(H, make_copy = FALSE)
|
||||
confusion.Activate(disease)
|
||||
TEST_ASSERT(H.get_confusion() > 0, "Human is not confused after getting symptom.")
|
||||
disease.cure()
|
||||
TEST_ASSERT_EQUAL(H.get_confusion(), 0, "Human is still confused after curing confusion.")
|
||||
|
||||
/datum/disease/advance/confusion/New()
|
||||
symptoms += new /datum/symptom/confusion
|
||||
Refresh()
|
||||
@@ -0,0 +1,25 @@
|
||||
/datum/unit_test/emoting
|
||||
var/emotes_used = 0
|
||||
|
||||
/datum/unit_test/emoting/Run()
|
||||
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
|
||||
RegisterSignal(human, COMSIG_MOB_EMOTE, .proc/on_emote_used)
|
||||
|
||||
human.say("*shrug")
|
||||
TEST_ASSERT_EQUAL(emotes_used, 1, "Human did not shrug")
|
||||
|
||||
human.say("*beep")
|
||||
TEST_ASSERT_EQUAL(emotes_used, 1, "Human beeped, when that should be restricted to silicons")
|
||||
|
||||
human.setOxyLoss(140)
|
||||
|
||||
TEST_ASSERT(human.stat != CONSCIOUS, "Human is somehow conscious after receiving suffocation damage")
|
||||
|
||||
human.say("*shrug")
|
||||
TEST_ASSERT_EQUAL(emotes_used, 1, "Human shrugged while unconscious")
|
||||
|
||||
human.say("*deathgasp")
|
||||
TEST_ASSERT_EQUAL(emotes_used, 2, "Human could not deathgasp while unconscious")
|
||||
|
||||
/datum/unit_test/emoting/proc/on_emote_used()
|
||||
emotes_used += 1
|
||||
@@ -0,0 +1,21 @@
|
||||
/// This test checks all heretic knowledge nodes - excluding the ones which are unreachable on purpose - and ensures players can reach them in game.
|
||||
/// If it finds a node that is unreachable, it throws an error.
|
||||
/datum/unit_test/heretic_knowledge/Run()
|
||||
///List of all knowledge excluding the unreachable base types.
|
||||
var/list/blacklist = list(/datum/eldritch_knowledge/spell,/datum/eldritch_knowledge/curse,/datum/eldritch_knowledge/final,/datum/eldritch_knowledge/summon)
|
||||
var/list/all_possible_knowledge = subtypesof(/datum/eldritch_knowledge) - blacklist
|
||||
|
||||
var/list/list_to_check = GLOB.heretic_start_knowledge.Copy()
|
||||
var/i = 0
|
||||
while(i < length(list_to_check))
|
||||
var/datum/eldritch_knowledge/eldritch_knowledge = allocate(list_to_check[++i])
|
||||
for(var/next_knowledge in eldritch_knowledge.next_knowledge)
|
||||
if(next_knowledge in list_to_check)
|
||||
continue
|
||||
list_to_check += next_knowledge
|
||||
|
||||
if(length(all_possible_knowledge) != length(all_possible_knowledge & list_to_check))
|
||||
var/list/unreachables = all_possible_knowledge - list_to_check
|
||||
for(var/X in unreachables)
|
||||
var/datum/eldritch_knowledge/eldritch_knowledge = X
|
||||
Fail("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!")
|
||||
@@ -0,0 +1,33 @@
|
||||
// test Jewish holiday
|
||||
/datum/unit_test/hanukkah_2123/Run()
|
||||
var/datum/holiday/hebrew/hanukkah/hanukkah = new
|
||||
TEST_ASSERT(hanukkah.shouldCelebrate(14, DECEMBER, 2123, 2, TUESDAY), "December 14, 2123 was not Hanukkah.")
|
||||
|
||||
// test Islamic holiday
|
||||
/datum/unit_test/ramadan_2165/Run()
|
||||
var/datum/holiday/islamic/ramadan/ramadan = new
|
||||
TEST_ASSERT(ramadan.shouldCelebrate(6, NOVEMBER, 2165, 1, WEDNESDAY), "November 6, 2165 was not Ramadan.")
|
||||
|
||||
// nth day of week
|
||||
/datum/unit_test/thanksgiving_2020/Run()
|
||||
var/datum/holiday/nth_week/thanksgiving/thanksgiving = new
|
||||
TEST_ASSERT(thanksgiving.shouldCelebrate(26, NOVEMBER, 2020, 4, THURSDAY), "November 26, 2020 was not Thanksgiving.")
|
||||
|
||||
// another nth day of week
|
||||
/datum/unit_test/indigenous_3683/Run()
|
||||
var/datum/holiday/nth_week/indigenous/indigenous = new
|
||||
TEST_ASSERT(indigenous.shouldCelebrate(11, OCTOBER, 3683, 2, MONDAY), "October 11, 3683 was not Indigenous Peoples' Day.")
|
||||
|
||||
// plain old simple holiday
|
||||
/datum/unit_test/hello_2020/Run()
|
||||
var/datum/holiday/hello/hello = new
|
||||
TEST_ASSERT(hello.shouldCelebrate(21, NOVEMBER, 2020, 3, SATURDAY), "November 21, 2020 was not Hello day.")
|
||||
|
||||
// holiday which goes across months
|
||||
/datum/unit_test/new_year_1983/Run()
|
||||
var/datum/holiday/new_year/new_year = new
|
||||
TEST_ASSERT(new_year.shouldCelebrate(2, JANUARY, 1983, 1, SUNDAY), "January 2, 1983 was not New Year.")
|
||||
|
||||
/datum/unit_test/moth_week_2020/Run()
|
||||
var/datum/holiday/moth/moth = new
|
||||
TEST_ASSERT(moth.shouldCelebrate(19, JULY, 2020, 3, SATURDAY), "July 19, 2020 was not Moth Week.")
|
||||
@@ -0,0 +1,11 @@
|
||||
/datum/unit_test/initialize_sanity/Run()
|
||||
if(length(SSatoms.BadInitializeCalls))
|
||||
Fail("Bad Initialize() calls detected. Please read logs.")
|
||||
var/list/init_failures_to_text = list(
|
||||
"[BAD_INIT_QDEL_BEFORE]" = "Qdeleted Before Initialized",
|
||||
"[BAD_INIT_DIDNT_INIT]" = "Did Not Initialize",
|
||||
"[BAD_INIT_SLEPT]" = "Initialize() Slept",
|
||||
"[BAD_INIT_NO_HINT]" = "No Initialize() Hint Returned",
|
||||
)
|
||||
for(var/failure in SSatoms.BadInitializeCalls)
|
||||
log_world("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets?
|
||||
@@ -0,0 +1,6 @@
|
||||
/datum/unit_test/keybinding_init/Run()
|
||||
for(var/i in subtypesof(/datum/keybinding))
|
||||
var/datum/keybinding/KB = i
|
||||
if(initial(KB.keybind_signal) || !initial(KB.name))
|
||||
continue
|
||||
Fail("[KB.name] does not have a keybind signal defined.")
|
||||
@@ -3,11 +3,10 @@
|
||||
var/obj/machinery/freezer = allocate(/obj/machinery/atmospherics/components/unary/thermomachine/freezer)
|
||||
|
||||
var/turf/freezer_location = freezer.loc
|
||||
freezer_location.ChangeTurf(/turf/open/floor/plasteel)
|
||||
freezer.deconstruct()
|
||||
|
||||
// Check that the components are created
|
||||
TEST_ASSERT(locate(/obj/item/stock_parts/micro_laser) in freezer_location, "Couldn't find micro-laser when disassembling freezer")
|
||||
|
||||
// Check that the circuit board itself is created
|
||||
TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine/freezer) in freezer_location, "Couldn't find the circuit board when disassembling freezer")
|
||||
TEST_ASSERT(locate(/obj/item/circuitboard/machine/thermomachine) in freezer_location, "Couldn't find the circuit board when disassembling freezer")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/datum/unit_test/merge_type/Run()
|
||||
var/list/blacklist = list(/obj/item/stack/sheet,
|
||||
/obj/item/stack/sheet/mineral,
|
||||
/obj/item/stack/ore,
|
||||
/obj/item/stack/spacecash,
|
||||
// /obj/item/stack/license_plates,
|
||||
/obj/item/stack/tile/mineral,
|
||||
/obj/item/stack/tile)
|
||||
|
||||
var/list/paths = subtypesof(/obj/item/stack) - blacklist
|
||||
|
||||
for(var/stackpath in paths)
|
||||
var/obj/item/stack/stack = stackpath
|
||||
if(!initial(stack.merge_type))
|
||||
Fail("([stack]) lacks set merge_type variable!")
|
||||
@@ -17,3 +17,22 @@
|
||||
/datum/unit_test/metabolization/Destroy()
|
||||
SSmobs.ignite()
|
||||
return ..()
|
||||
|
||||
/datum/unit_test/on_mob_end_metabolize/Run()
|
||||
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill)
|
||||
var/datum/reagent/drug/methamphetamine/meth = /datum/reagent/drug/methamphetamine
|
||||
|
||||
// Give them enough meth to be consumed in 2 metabolizations
|
||||
pill.reagents.add_reagent(meth, initial(meth.metabolization_rate) * 1.9)
|
||||
pill.attack(user, user)
|
||||
|
||||
user.Life()
|
||||
|
||||
TEST_ASSERT(user.reagents.has_reagent(meth), "User does not have meth in their system after consuming it")
|
||||
TEST_ASSERT(user.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "User consumed meth, but did not gain movespeed modifier")
|
||||
|
||||
user.Life()
|
||||
|
||||
TEST_ASSERT(!user.reagents.has_reagent(meth), "User still has meth in their system when it should've finished metabolizing")
|
||||
TEST_ASSERT(!user.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "User still has movespeed modifier despite not containing any more meth")
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
CHECK_OUTFIT_SLOT(glasses, ITEM_SLOT_EYES)
|
||||
CHECK_OUTFIT_SLOT(id, ITEM_SLOT_ID)
|
||||
CHECK_OUTFIT_SLOT(suit_store, ITEM_SLOT_SUITSTORE)
|
||||
CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_POCKET)
|
||||
CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_POCKET)
|
||||
CHECK_OUTFIT_SLOT(l_pocket, ITEM_SLOT_LPOCKET)
|
||||
CHECK_OUTFIT_SLOT(r_pocket, ITEM_SLOT_RPOCKET)
|
||||
|
||||
if (outfit.backpack_contents || outfit.box)
|
||||
var/list/backpack_contents = outfit.backpack_contents?.Copy()
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/datum/unit_test/pills/Run()
|
||||
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/reagent_containers/pill/iron/pill = allocate(/obj/item/reagent_containers/pill/iron)
|
||||
|
||||
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/iron), FALSE, "Human somehow has iron before taking pill")
|
||||
|
||||
pill.attack(human, human)
|
||||
human.Life()
|
||||
|
||||
TEST_ASSERT(human.has_reagent(/datum/reagent/iron), "Human doesn't have iron after taking pill")
|
||||
@@ -0,0 +1,5 @@
|
||||
/datum/unit_test/projectile_movetypes/Run()
|
||||
for(var/path in typesof(/obj/item/projectile))
|
||||
var/obj/projectile/projectile = path
|
||||
if(initial(projectile.movement_type) & PHASING)
|
||||
Fail("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!")
|
||||
@@ -0,0 +1,59 @@
|
||||
// testing the mob expose procs are working
|
||||
|
||||
/datum/reagent/method_patch_test
|
||||
name = "method patch test"
|
||||
|
||||
/datum/reagent/method_patch_test/expose_mob(mob/living/target, methods = PATCH, reac_volume, show_message = TRUE)
|
||||
. = ..()
|
||||
if(methods & PATCH)
|
||||
target.health = 90
|
||||
if(methods & INJECT)
|
||||
target.health = 80
|
||||
|
||||
/datum/unit_test/reagent_mob_expose/Run()
|
||||
// Life() is handled just by tests
|
||||
SSmobs.pause()
|
||||
|
||||
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/reagent_containers/dropper/dropper = allocate(/obj/item/reagent_containers/dropper)
|
||||
var/obj/item/reagent_containers/food/drinks/drink = allocate(/obj/item/reagent_containers/food/drinks/bottle)
|
||||
var/obj/item/reagent_containers/pill/patch/patch = allocate(/obj/item/reagent_containers/pill/patch)
|
||||
var/obj/item/reagent_containers/syringe/syringe = allocate(/obj/item/reagent_containers/syringe)
|
||||
|
||||
// INGEST
|
||||
TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human has fire stacks before taking phlogiston")
|
||||
drink.reagents.add_reagent(/datum/reagent/phlogiston, 10)
|
||||
drink.attack(human, human)
|
||||
TEST_ASSERT_EQUAL(human.fire_stacks, 1, "Human does not have fire stacks after taking phlogiston")
|
||||
human.Life()
|
||||
TEST_ASSERT(human.fire_stacks > 1, "Human fire stacks did not increase after life tick")
|
||||
|
||||
// TOUCH
|
||||
dropper.reagents.add_reagent(/datum/reagent/water, 1)
|
||||
dropper.afterattack(human, human, TRUE)
|
||||
TEST_ASSERT_EQUAL(human.fire_stacks, 0, "Human still has fire stacks after touching water")
|
||||
|
||||
// VAPOR
|
||||
TEST_ASSERT_EQUAL(human.drowsyness, 0, "Human is drowsy at the start of testing")
|
||||
drink.reagents.clear_reagents()
|
||||
drink.reagents.add_reagent(/datum/reagent/nitrous_oxide, 10)
|
||||
drink.reagents.trans_to(human, 10, methods = VAPOR)
|
||||
TEST_ASSERT_NOTEQUAL(human.drowsyness, 0, "Human is not drowsy after exposure to vapors")
|
||||
|
||||
// PATCH
|
||||
human.health = 100
|
||||
TEST_ASSERT_EQUAL(human.health, 100, "Human health did not set properly")
|
||||
patch.reagents.add_reagent(/datum/reagent/method_patch_test, 1)
|
||||
patch.self_delay = 0
|
||||
patch.attack(human, human)
|
||||
TEST_ASSERT_EQUAL(human.health, 90, "Human health did not update after patch was applied")
|
||||
|
||||
// INJECT
|
||||
syringe.reagents.add_reagent(/datum/reagent/method_patch_test, 1)
|
||||
syringe.mode = SYRINGE_INJECT
|
||||
syringe.afterattack(human, human, TRUE)
|
||||
TEST_ASSERT_EQUAL(human.health, 80, "Human health did not update after injection from syringe")
|
||||
|
||||
/datum/unit_test/reagent_mob_expose/Destroy()
|
||||
SSmobs.ignite()
|
||||
return ..()
|
||||
@@ -0,0 +1,12 @@
|
||||
/datum/unit_test/reagent_mob_procs/Run()
|
||||
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
|
||||
var/obj/item/food/hotdog/debug/fooditem = allocate(/obj/item/food/hotdog/debug)
|
||||
|
||||
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human somehow has ketchup before eating")
|
||||
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/medicine/epinephrine), FALSE, "Human somehow has epinephrine before injecting")
|
||||
|
||||
fooditem.attack(human, human)
|
||||
human.reagents.add_reagent(/datum/reagent/medicine/epinephrine, 5)
|
||||
|
||||
TEST_ASSERT(human.has_reagent(/datum/reagent/consumable/ketchup), "Human doesn't have ketchup after eating")
|
||||
TEST_ASSERT(human.has_reagent(/datum/reagent/medicine/epinephrine), "Human doesn't have epinephrine after injecting")
|
||||
@@ -10,7 +10,6 @@
|
||||
test(";%Never gonna give you up", "Never gonna give you up", list(MODE_HEADSET = TRUE, MODE_SING = TRUE))
|
||||
test(".s Gun plz", "Gun plz", list(RADIO_KEY = RADIO_KEY_SECURITY, RADIO_EXTENSION = RADIO_CHANNEL_SECURITY))
|
||||
test("...What", "...What", list())
|
||||
//note to lettern: add the ++, ||, __, and the verb*text checks
|
||||
|
||||
/datum/unit_test/get_message_mods/proc/test(message, expected_message, list/expected_mods)
|
||||
var/list/mods = list()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Check that standard food items fit on the serving tray
|
||||
*/
|
||||
/datum/unit_test/servingtray/Run()
|
||||
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human)
|
||||
var/obj/structure/table/the_table = allocate(/obj/structure/table)
|
||||
var/obj/item/storage/bag/tray/test_tray = allocate(/obj/item/storage/bag/tray)
|
||||
var/obj/item/reagent_containers/food/banana = allocate(/obj/item/food/rationpack)
|
||||
var/obj/item/food/the_bread = allocate(/obj/item/food/breadslice)
|
||||
var/obj/item/reagent_containers/food/sugarcookie = allocate(/obj/item/food/cookie/sugar)
|
||||
var/obj/item/clothing/under/jumpsuit = allocate(/obj/item/clothing/under/color/black)
|
||||
|
||||
TEST_ASSERT_EQUAL((the_bread in test_tray.contents), FALSE, "The bread is on the serving tray at test start")
|
||||
|
||||
// set the tray to single item mode the dirty way
|
||||
var/datum/component/storage/tray_storage = test_tray.GetComponent(/datum/component/storage)
|
||||
tray_storage.collection_mode = COLLECT_ONE
|
||||
|
||||
test_tray.pre_attack(the_bread, human)
|
||||
|
||||
TEST_ASSERT_EQUAL((the_bread in test_tray.contents), TRUE, "The bread did not get picked up by the serving tray")
|
||||
|
||||
test_tray.pre_attack(banana, human)
|
||||
|
||||
TEST_ASSERT_EQUAL((banana in test_tray.contents), TRUE, "The banana did not get picked up by the serving tray")
|
||||
|
||||
the_table.attackby(test_tray, human)
|
||||
|
||||
TEST_ASSERT_EQUAL(test_tray.contents.len, 0, "The serving tray did not drop all items on hitting the table")
|
||||
|
||||
test_tray.pre_attack(sugarcookie, human)
|
||||
|
||||
TEST_ASSERT_EQUAL((sugarcookie in test_tray.contents), TRUE, "The sugarcookie did not get picked up by the serving tray")
|
||||
|
||||
human.equip_to_slot(jumpsuit, ITEM_SLOT_ICLOTHING)
|
||||
TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_ICLOTHING), "Human does not have jumpsuit on")
|
||||
|
||||
human.equip_to_slot(test_tray, ITEM_SLOT_LPOCKET)
|
||||
TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_LPOCKET), "Serving tray failed to fit in the Left Pocket")
|
||||
|
||||
human.equip_to_slot(test_tray, ITEM_SLOT_RPOCKET)
|
||||
TEST_ASSERT(human.get_item_by_slot(ITEM_SLOT_RPOCKET), "Serving tray failed to fit in the Right Pocket")
|
||||
|
||||
test_tray.attack(human, human)
|
||||
|
||||
TEST_ASSERT_EQUAL(test_tray.contents.len, 0, "The serving tray did not drop all items on hitting a human")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/datum/unit_test/siunit/Run()
|
||||
TEST_ASSERT_EQUAL(siunit(0.5345, "A", 0), "535 mA", "")
|
||||
TEST_ASSERT_EQUAL(siunit(0.5344, "A", 0), "534 mA", "")
|
||||
TEST_ASSERT_EQUAL(siunit(-0.5344, "A", 0), "-534 mA", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1.234, 1), "1.2 kPa", "") // test for pascal require *10e-3, as the game thinks in kPa, the proc siunit in Pa
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1.234, 2), "1.23 kPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1.234, 3), "1.234 kPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1, 4), "1 kPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(0), "0 Pa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1e3), "1 MPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(999e3), "999 MPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(999.9e3), "999.9 MPa" , "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(999.9e3, 0), "1 GPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(1e6), "1 GPa", "")
|
||||
TEST_ASSERT_EQUAL(siunit_pressure(3e17), "300000 PPa", "")
|
||||
@@ -1,7 +1,7 @@
|
||||
/datum/unit_test/spawn_humans/Run()
|
||||
var/locs = block(run_loc_bottom_left, run_loc_top_right)
|
||||
var/locs = block(run_loc_bottom_left, run_loc_top_right)
|
||||
|
||||
for(var/I in 1 to 5)
|
||||
new /mob/living/carbon/human(pick(locs))
|
||||
for(var/I in 1 to 5)
|
||||
new /mob/living/carbon/human(pick(locs))
|
||||
|
||||
sleep(50)
|
||||
sleep(50)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/datum/unit_test/species_whitelist_check/Run()
|
||||
for(var/typepath in subtypesof(/datum/species))
|
||||
var/datum/species/S = typepath
|
||||
if(initial(S.changesource_flags) == NONE)
|
||||
Fail("A species type was detected with no changesource flags: [S]")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user