Merge branch 'master' into more-ashwalker-woes

This commit is contained in:
Putnam3145
2021-10-18 04:35:57 -07:00
committed by GitHub
275 changed files with 21641 additions and 18315 deletions
-40
View File
@@ -435,18 +435,7 @@
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller=1'>(Force Storyteller)</A><br>"
if (GLOB.dynamic_forced_storyteller)
var/datum/dynamic_storyteller/S = GLOB.dynamic_forced_storyteller
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_storyteller_clear=1'>-> [initial(S.name)] <-</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_options=1'>(Dynamic mode options)</A><br>"
else if (SSticker.IsRoundInProgress())
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if (mode.forced_latejoin_rule)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin_clear=1'>-> [mode.forced_latejoin_rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_midround=1'>(Execute Midround Ruleset!)</A><br>"
dat += "<hr/>"
if(SSticker.IsRoundInProgress())
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
@@ -711,20 +700,6 @@
log_admin("[key_name(usr)] set the pre-game delay to [DisplayTimeText(newtime)].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Delay Game Start") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggledynamicvote()
set category = "Server"
set desc="Switches between secret/extended and dynamic voting"
set name="Toggle Dynamic Vote"
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
if (!prev_dynamic_voting)
to_chat(world, "<B>Vote is now between dynamic storytellers.</B>")
else
to_chat(world, "<B>Vote is now between extended and secret.</B>")
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] toggled dynamic voting.</span>")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Dynamic Voting", "[prev_dynamic_voting ? "Disabled" : "Enabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/unprison(mob/M in GLOB.mob_list)
set category = "Admin"
set name = "Unprison"
@@ -941,27 +916,12 @@
<b>No stacking:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_no_stacking=1'> <b>[GLOB.dynamic_no_stacking ? "ON" : "OFF"]</b></a>.
<br/>Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted. <br/>
<br/>
<b>Classic secret mode:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_classic_secret=1'> <b>[GLOB.dynamic_classic_secret ? "ON" : "OFF"]</b></a>.
<br/>Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn. <br/>
<br/>
<br/>
<b>Forced threat level:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_forced_threat=1'><b>[GLOB.dynamic_forced_threat_level]</b></a>.
<br/>The value threat is set to if it is higher than -1.<br/>
<br/>
<b>High population limit:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_high_pop_limit=1'><b>[GLOB.dynamic_high_pop_limit]</b></a>.
<br/>The threshold at which "high population override" will be in effect. <br/>
<br/>
<b>Stacking threeshold:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_stacking_limit=1'><b>[GLOB.dynamic_stacking_limit]</b></a>.
<br/>The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens. <br/>
<h3>Advanced parameters</h3>
Curve centre: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_centre=1'>-> [GLOB.dynamic_curve_centre] <-</A><br>
Curve width: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_width=1'>-> [GLOB.dynamic_curve_width] <-</A><br>
Latejoin injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_min=1'>-> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_max=1'>-> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <-</A> Minutes<br>
Midround injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_min=1'>-> [GLOB.dynamic_midround_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_max=1'>-> [GLOB.dynamic_midround_delay_max / 60 / 10] <-</A> Minutes<br>
"}
user << browse(dat, "window=dyn_mode_options;size=900x650")
+4 -3
View File
@@ -135,7 +135,6 @@ GLOBAL_PROTECT(admin_verbs_server)
/client/proc/everyone_random,
/datum/admins/proc/toggleAI,
/datum/admins/proc/toggleMulticam, //CIT
/datum/admins/proc/toggledynamicvote, //CIT
/client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/
/client/proc/cmd_debug_del_all,
/client/proc/toggle_random_events,
@@ -190,9 +189,11 @@ GLOBAL_PROTECT(admin_verbs_debug)
// /client/proc/validate_cards,
// /client/proc/test_cardpack_distribution,
// /client/proc/print_cards,
// #ifdef TESTING
#ifdef TESTING
// /client/proc/check_missing_sprites,
// #endif
// /client/proc/export_dynamic_json,
/client/proc/run_dynamic_simulations,
#endif
/datum/admins/proc/create_or_modify_area,
/datum/admins/proc/fixcorruption,
#ifdef EXTOOLS_REFERENCE_TRACKING
+1 -1
View File
@@ -77,7 +77,7 @@ GLOBAL_VAR(antag_prototypes)
return common_commands
/datum/mind/proc/get_special_statuses()
var/list/result = list()
var/list/result = LAZYCOPY(special_statuses)
if(!current)
result += "<span class='bad'>No body!</span>"
if(current && HAS_TRAIT(current, TRAIT_MINDSHIELD))
+8 -234
View File
@@ -1375,15 +1375,13 @@
else if(href_list["f_dynamic_roundstart"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
if(SSticker?.mode)
return tgui_alert(usr, "The game has already started.")
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
var/datum/dynamic_ruleset/roundstart/newrule = new rule()
roundstart_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules
var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in sortList(roundstart_rules)
if (added_rule)
GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.")
@@ -1407,201 +1405,18 @@
log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.")
message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1)
else if(href_list["f_dynamic_storyteller"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
var/list/choices = list()
for(var/T in config.storyteller_cache)
var/datum/dynamic_storyteller/S = T
choices[initial(S.name)] = T
var/choice = choices[input("Select storyteller:", "Storyteller", "Classic") as null|anything in choices]
if(choice)
GLOB.dynamic_forced_storyteller = choice
log_admin("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
message_admins("[key_name(usr)] forced the storyteller to [GLOB.dynamic_forced_storyteller].")
Game()
else if(href_list["f_dynamic_storyteller_clear"])
if(!check_rights(R_ADMIN))
return
GLOB.dynamic_forced_storyteller = null
Game()
log_admin("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.")
message_admins("[key_name(usr)] cleared the forced storyteller. The mode will pick one as normal.", 1)
else if(href_list["f_dynamic_latejoin"])
if(!check_rights(R_ADMIN))
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/latejoin_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
var/datum/dynamic_ruleset/latejoin/newrule = new rule()
latejoin_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules
if (added_rule)
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.forced_latejoin_rule = latejoin_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.")
message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1)
Game()
else if(href_list["f_dynamic_latejoin_clear"])
if(!check_rights(R_ADMIN))
return
if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.forced_latejoin_rule = null
Game()
log_admin("[key_name(usr)] cleared the forced latejoin ruleset.")
message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1)
else if(href_list["f_dynamic_midround"])
if(!check_rights(R_ADMIN))
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/midround_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
var/datum/dynamic_ruleset/midround/newrule = new rule()
midround_rules[newrule.name] = rule
var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules
if (added_rule)
var/datum/game_mode/dynamic/mode = SSticker.mode
log_admin("[key_name(usr)] executed the [added_rule] ruleset.")
message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1)
mode.picking_specific_rule(midround_rules[added_rule],1)
else if (href_list["f_dynamic_options"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
if(SSticker?.mode)
return tgui_alert(usr, "The game has already started.")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_centre"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number is allowed. This is adjusted by dynamic voting.", "Change curve centre", null) as num
log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].")
message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1)
GLOB.dynamic_curve_centre = new_centre
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_width"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num
if (new_width < 0.5 || new_width > 4)
return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null)
log_admin("[key_name(usr)] changed the distribution curve width to [new_width].")
message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1)
GLOB.dynamic_curve_width = new_width
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_latejoin_min"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num
if(new_min <= 0)
return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max)
return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.")
message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1)
GLOB.dynamic_latejoin_delay_min = (new_min MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_latejoin_max"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num
if(new_max <= 0)
return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min)
return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.")
message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1)
GLOB.dynamic_latejoin_delay_max = (new_max MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_midround_min"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num
if(new_min <= 0)
return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
if((new_min MINUTES) > GLOB.dynamic_midround_delay_max)
return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.")
message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1)
GLOB.dynamic_midround_delay_min = (new_min MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_midround_max"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num
if(new_max <= 0)
return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
if((new_max MINUTES) > GLOB.dynamic_midround_delay_max)
return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.")
message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1)
GLOB.dynamic_midround_delay_max = (new_max MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_force_extended"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended
log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].")
message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].")
@@ -1611,70 +1426,29 @@
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking
log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].")
message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_classic_secret"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret
log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_stacking_limit"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num
log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].")
message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_high_pop_limit"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num
if (new_value < 0)
return alert(usr, "Only positive values allowed!", null, null, null, null)
GLOB.dynamic_high_pop_limit = new_value
log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_forced_threat"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
if(SSticker?.mode)
return tgui_alert(usr, "The game has already started.")
var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num
if (new_value > 100)
return alert(usr, "The value must be be under 100.", null, null, null, null)
return tgui_alert(usr, "The value must be be under 100.")
GLOB.dynamic_forced_threat_level = new_value
log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].")
+38 -4
View File
@@ -161,13 +161,14 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/client/initiator //semi-misnomer, it's the person who ahelped/was bwoinked
var/initiator_ckey
var/initiator_key_name
var/heard_by_no_admins = FALSE
var/list/_interactions //use AddInteraction() or, preferably, admin_ticket_log()
var/obj/effect/statclick/ahelp/statclick
var/static/ticket_counter = 0
/// did we send "answered" to irc yet
var/answered = FALSE
//call this on its own to create a ticket, don't manually assign current_ticket
//msg is the title of the ticket: usually the ahelp text
@@ -209,7 +210,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
if(admin_number_present <= 0)
to_chat(C, "<span class='notice'>No active admins are online, your adminhelp was sent to the admin irc.</span>")
heard_by_no_admins = TRUE
else
// citadel edit: send anyways
send2adminchat(initiator_ckey, "Ticket #[id]: [name] - Heard by [admin_number_present] admins present with +BAN.")
GLOB.ahelp_tickets.active_tickets += src
@@ -220,8 +223,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return ..()
/datum/admin_help/proc/AddInteraction(formatted_message)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
if(usr && (usr.ckey != initiator_ckey) && !answered)
answered = TRUE
send2adminchat(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
@@ -728,3 +731,34 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return founds
return msg
/**
* Checks a given message to see if any of the words contain an active admin's ckey with an @ before it
*
* Returns nothing if no pings are found, otherwise returns an associative list with ckey -> client
* Also modifies msg to underline the pings, then stores them in the key [ADMINSAY_PING_UNDERLINE_NAME_INDEX] for returning
*
* Arguments:
* * msg - the message being scanned
*/
/proc/check_admin_pings(msg)
//explode the input msg into a list
var/list/msglist = splittext(msg, " ")
var/list/admins_to_ping = list()
var/i = 0
for(var/word in msglist)
i++
if(!length(word))
continue
if(word[1] != "@")
continue
var/ckey_check = lowertext(copytext(word, 2))
var/client/client_check = GLOB.directory[ckey_check]
if(client_check?.holder)
msglist[i] = "<u>[word]</u>"
admins_to_ping[ckey_check] = client_check
if(length(admins_to_ping))
admins_to_ping[ADMINSAY_PING_UNDERLINE_NAME_INDEX] = jointext(msglist, " ") // without tuples, we must make do!
return admins_to_ping
+13
View File
@@ -8,6 +8,19 @@
msg = copytext_char(sanitize(msg), 1, MAX_MESSAGE_LEN)
if(!msg)
return
var/list/pinged_admin_clients = check_admin_pings(msg)
if(length(pinged_admin_clients) && pinged_admin_clients[ADMINSAY_PING_UNDERLINE_NAME_INDEX])
msg = pinged_admin_clients[ADMINSAY_PING_UNDERLINE_NAME_INDEX]
pinged_admin_clients -= ADMINSAY_PING_UNDERLINE_NAME_INDEX
for(var/iter_ckey in pinged_admin_clients)
var/client/iter_admin_client = pinged_admin_clients[iter_ckey]
if(!iter_admin_client?.holder)
continue
window_flash(iter_admin_client)
SEND_SOUND(iter_admin_client.mob, sound('sound/misc/bloop.ogg'))
msg = emoji_parse(msg)
mob.log_talk(msg, LOG_ASAY)
@@ -55,9 +55,15 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/specialization(datum/mind/new_owner)
return src
///Called by the transfer_to() mind proc after the mind (mind.current and new_character.mind) has moved but before the player (key and client) is transfered.
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
SHOULD_CALL_PARENT(TRUE)
remove_innate_effects(old_body)
if(old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
apply_innate_effects(new_body)
if(new_body.stat != DEAD)
new_body.add_to_current_living_antags()
//This handles the application of antag huds/special abilities
/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
@@ -94,8 +100,9 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/create_team(datum/team/team)
return
//Proc called when the datum is given to a mind.
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
set waitfor = FALSE
if(!(owner?.current))
return
@@ -113,6 +120,8 @@ GLOBAL_LIST_EMPTY(antagonists)
if(istype(M))
M.name = "[name] Training"
owner.current.AddComponent(/datum/component/activity)
if(owner.current.stat != DEAD)
owner.current.add_to_current_living_antags()
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M)
@@ -131,13 +140,17 @@ GLOBAL_LIST_EMPTY(antagonists)
owner.current.ghostize(0)
C.transfer_ckey(owner.current, FALSE)
///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
/datum/antagonist/proc/on_removal()
SHOULD_CALL_PARENT(TRUE)
remove_innate_effects()
clear_antag_moodies()
if(owner)
LAZYREMOVE(owner.antag_datums, src)
for(var/A in skill_modifiers)
owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
if(!LAZYLEN(owner.antag_datums))
owner.current.remove_from_current_living_antags()
if(!silent && owner.current)
farewell()
var/datum/team/team = get_team()
@@ -226,16 +226,16 @@
user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shed_human_form)
if(!ishuman(user))
return
var/mob/living/carbon/human/H = user
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
var/datum/antagonist/heretic/heretic = user.mind.has_antag_datum(/datum/antagonist/heretic)
var/datum/eldritch_knowledge/flesh_grasp/ghoul1 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_grasp)
ghoul1.ghoul_amt *= 3
var/datum/eldritch_knowledge/flesh_ghoul/ghoul2 = heretic.get_knowledge(/datum/eldritch_knowledge/flesh_ghoul)
ghoul2.max_amt *= 3
var/mob/living/carbon/human/lord_of_arms = user
lord_of_arms.physiology.brute_mod *= 0.5
lord_of_arms.physiology.burn_mod *= 0.5
lord_of_arms.client?.give_award(/datum/award/achievement/misc/flesh_ascension, lord_of_arms)
var/datum/antagonist/heretic/heretic_datum = user.mind.has_antag_datum(/datum/antagonist/heretic)
var/datum/eldritch_knowledge/flesh_grasp/grasp_ghoul = heretic_datum.get_knowledge(/datum/eldritch_knowledge/flesh_grasp)
grasp_ghoul.ghoul_amt *= 3
var/datum/eldritch_knowledge/flesh_ghoul/better_ghoul = heretic_datum.get_knowledge(/datum/eldritch_knowledge/flesh_ghoul)
better_ghoul.max_amt *= 3
return ..()
/datum/eldritch_knowledge/flesh_blade_upgrade_2
name = "Remembrance"
@@ -181,13 +181,13 @@
var/mob/living/carbon/human/H = user
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
H.client?.give_award(/datum/award/achievement/misc/rust_ascension, H)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for the Rustbringer, [user.real_name] has ascended! None shall escape the corrosion! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
new /datum/rust_spread(loc)
var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic)
ascension.ascended = TRUE
return ..()
/datum/eldritch_knowledge/final/rust_final/on_life(mob/user)
. = ..()
if(!finished)
@@ -183,14 +183,14 @@
var/datum/weather/void_storm/storm
/datum/eldritch_knowledge/final/void_final/on_finished_recipe(mob/living/user, list/atoms, loc)
var/mob/living/carbon/human/H = user
user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch)
H.physiology.brute_mod *= 0.5
H.physiology.burn_mod *= 0.5
ADD_TRAIT(H, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# The nobleman of void [H.real_name] has arrived, step along the Waltz that ends worlds! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
sound_loop = new(list(user),TRUE,TRUE)
var/mob/living/carbon/human/waltzing = user
waltzing.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/repulse/eldritch)
waltzing.physiology.brute_mod *= 0.5
waltzing.physiology.burn_mod *= 0.5
ADD_TRAIT(waltzing, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT)
waltzing.client?.give_award(/datum/award/achievement/misc/void_ascension, waltzing)
priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# The nobleman of void [waltzing.real_name] has arrived, step along the Waltz that ends worlds! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg')
sound_loop = new(user, TRUE, TRUE)
return ..()
/datum/eldritch_knowledge/final/void_final/on_death()
@@ -94,15 +94,6 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
if(board.moved)
to_chat(user, "The shuttle has already been moved! You have forfeit the right to declare war.")
return FALSE
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
return FALSE
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
return FALSE
return TRUE
/obj/item/nuclear_challenge/clownops
+156 -19
View File
@@ -1,3 +1,5 @@
#define DECONVERTER_STATION_WIN "gamemode_station_win"
#define DECONVERTER_REVS_WIN "gamemode_revs_win"
//How often to check for promotion possibility
#define HEAD_UPDATE_PERIOD 300
@@ -10,6 +12,11 @@
threat = 2
var/hud_type = "rev"
var/datum/team/revolution/rev_team
///when this antagonist is being de-antagged, this is why
var/deconversion_reason
/// What message should the player receive when they are being demoted, and the revolution has won?
var/victory_message = "The revolution has overpowered the command staff! Viva la revolution! Execute any head of staff and security should you find them alive."
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
. = ..()
@@ -201,7 +208,22 @@
new_rev.silent = FALSE
to_chat(old_owner, "<span class='userdanger'>Revolution has been disappointed of your leader traits! You are a regular revolutionary now!</span>")
/// Checks if the revolution succeeded, and lets them know.
/datum/antagonist/rev/proc/announce_victorious()
. = rev_team.check_rev_victory()
if (!.)
return
to_chat(owner, "<span class='deconversion_message bold'>[victory_message]</span>")
var/policy = get_policy(ROLE_REV_SUCCESSFUL)
if (policy)
to_chat(owner, policy)
/datum/antagonist/rev/farewell()
if (announce_victorious())
return
if(ishuman(owner.current))
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel... You don't seem to be able to recall the names of your comrades, not even your leaders...</span>")
@@ -210,15 +232,17 @@
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and deletes your neural reprogramming! You remember nothing of your time spent reprogrammed, you can't even remember the names or identities of anyone involved...</span>")
/datum/antagonist/rev/head/farewell()
if((ishuman(owner.current) || ismonkey(owner.current)))
if (announce_victorious() || deconversion_reason == DECONVERTER_STATION_WIN)
return
if((ishuman(owner.current)))
if(owner.current.stat != DEAD)
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
owner.current.visible_message(span_deconversion_message("[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!"), null, null, null, owner.current)
to_chat(owner, "<span class ='deconversion_message bold'>You have given up your cause of overthrowing the command staff. You are no longer a Head Revolutionary.</span>")
else
to_chat(owner, "<span class ='deconversion_message bold'>The sweet release of death. You are no longer a Head Revolutionary.</span>")
else if(issilicon(owner.current))
owner.current.visible_message("<span class='deconversion_message'>The frame beeps contentedly, suppressing the disloyal personality traits from the MMI before initalizing it.</span>", null, null, null, owner.current)
to_chat(owner, "<span class='userdanger'>The frame's firmware detects and suppresses your unwanted personality traits! You feel more content with the leadership around these parts.</span>")
owner.current.visible_message(span_deconversion_message("The frame beeps contentedly, suppressing the disloyal personality traits from the MMI before initalizing it."), null, null, null, owner.current)
to_chat(owner, span_userdanger("The frame's firmware detects and suppresses your unwanted personality traits! You feel more content with the leadership around these parts."))
//blunt trauma deconversions call this through species.dm spec_attacked_by()
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
@@ -226,13 +250,14 @@
if(borged)
message_admins("[ADMIN_LOOKUPFLW(owner.current)] has been borged while being a [name]")
owner.special_role = null
if(iscarbon(owner.current))
if(iscarbon(owner.current) && deconverter != DECONVERTER_REVS_WIN)
var/mob/living/carbon/C = owner.current
C.Unconscious(100)
deconversion_reason = deconverter
owner.remove_antag_datum(type)
/datum/antagonist/rev/head/remove_revolutionary(borged,deconverter)
if(borged || deconverter == "gamemode")
if(borged || deconverter == DECONVERTER_STATION_WIN || deconverter == DECONVERTER_REVS_WIN)
. = ..()
/datum/antagonist/rev/head/equip_rev()
@@ -262,6 +287,21 @@
S.Insert(H, special = FALSE, drop_if_replaced = FALSE)
to_chat(H, "Your eyes have been implanted with a cybernetic security HUD which will help you keep track of who is mindshield-implanted, and therefore unable to be recruited.")
/// "Enemy of the Revolutionary", given to heads and security when the revolution wins
/datum/antagonist/revolution_enemy
name = "Enemy of the Revolution"
show_in_antagpanel = FALSE
/datum/antagonist/revolution_enemy/on_gain()
owner.special_role = "revolution enemy"
var/datum/objective/survive/survive = new /datum/objective/survive
survive.owner = owner
survive.explanation_text = "The station has been overrun by revolutionaries, stay alive until the end."
objectives += survive
return ..()
/datum/team/revolution
name = "Revolution"
var/max_headrevs = 3
@@ -314,6 +354,100 @@
ex_headrevs = get_antag_minds(/datum/antagonist/rev/head, TRUE)
ex_revs = get_antag_minds(/datum/antagonist/rev, TRUE)
/// Checks if revs have won
/datum/team/revolution/proc/check_rev_victory()
for(var/datum/objective/mutiny/objective in objectives)
if(!(objective.check_completion()))
return FALSE
return TRUE
/// Checks if heads have won
/datum/team/revolution/proc/check_heads_victory()
for(var/datum/mind/rev_mind in head_revolutionaries())
var/turf/rev_turf = get_turf(rev_mind.current)
if(!considered_afk(rev_mind) && considered_alive(rev_mind) && is_station_level(rev_turf.z))
if(ishuman(rev_mind.current))
return FALSE
return TRUE
/// Updates the state of the world depending on if revs won or loss.
/// Returns who won, at which case this method should no longer be called.
/// If revs_win_injection_amount is passed, then that amount of threat will be added if the revs win.
/datum/team/revolution/proc/process_victory(revs_win_injection_amount)
if (check_rev_victory())
. = REVOLUTION_VICTORY
else if (check_heads_victory())
. = STATION_VICTORY
else
return
SSshuttle.clearHostileEnvironment(src)
save_members()
// Remove everyone as a revolutionary
for (var/_rev_mind in members)
var/datum/mind/rev_mind = _rev_mind
if (rev_mind.has_antag_datum(/datum/antagonist/rev))
var/datum/antagonist/rev/rev_antag = rev_mind.has_antag_datum(/datum/antagonist/rev)
rev_antag.remove_revolutionary(FALSE, . == STATION_VICTORY ? DECONVERTER_STATION_WIN : DECONVERTER_REVS_WIN)
LAZYADD(rev_mind.special_statuses, "<span class='bad'>Former [(rev_mind in ex_headrevs) ? "head revolutionary" : "revolutionary"]</span>")
if (. == STATION_VICTORY)
// If the revolution was quelled, make rev heads unable to be revived through pods
for (var/_rev_head_mind in ex_revs)
var/datum/mind/rev_head_mind = _rev_head_mind
var/mob/living/carbon/rev_head_body = rev_head_mind.current
if(istype(rev_head_body) && rev_head_body.stat == DEAD)
rev_head_body.makeUncloneable()
priority_announce("It appears the mutiny has been quelled. Please return yourself and your incapacitated colleagues to work. \
We have remotely blacklisted the head revolutionaries in your medical records to prevent accidental revival.", null, 'sound/announcer/classic/attention.ogg', null, "Central Command Loyalty Monitoring Division")
else
for (var/_player in GLOB.player_list)
var/mob/player = _player
var/datum/mind/mind = player.mind
if (isnull(mind))
continue
if (!(mind.assigned_role in GLOB.command_positions + GLOB.security_positions))
continue
var/mob/living/carbon/target_body = mind.current
mind.add_antag_datum(/datum/antagonist/revolution_enemy)
if (!istype(target_body))
continue
if (target_body.stat == DEAD)
target_body.makeUncloneable()
else
mind.announce_objectives()
for (var/job_name in GLOB.command_positions + GLOB.security_positions)
var/datum/job/job = SSjob.GetJob(job_name)
job.allow_bureaucratic_error = FALSE
job.total_positions = 0
if (revs_win_injection_amount)
var/datum/game_mode/dynamic/dynamic = SSticker.mode
dynamic.create_threat(revs_win_injection_amount)
dynamic.threat_log += "[worldtime2text()]: Revolution victory. Added [revs_win_injection_amount] threat."
priority_announce("A recent assessment of your station has marked your station as a severe risk area for high ranking Nanotrasen officials. \
For the safety of our staff, we have blacklisted your station for new employment of security and command. \
[pick(world.file2list("strings/anti_union_propaganda.txt"))]", null, 'sound/announcer/classic/attention.ogg', null, "Central Command Loyalty Monitoring Division")
/// Mutates the ticker to report that the revs have won
/datum/team/revolution/proc/round_result(finished)
if (finished == REVOLUTION_VICTORY)
SSticker.mode_result = "win - heads killed"
SSticker.news_report = REVS_WIN
else if (finished == STATION_VICTORY)
SSticker.mode_result = "loss - rev heads killed"
SSticker.news_report = REVS_LOSE
/datum/team/revolution/roundend_report()
if(!members.len && !ex_headrevs.len)
return
@@ -322,18 +456,6 @@
result += "<div class='panel redborder'>"
var/num_revs = 0
var/num_survivors = 0
for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
if(survivor.ckey)
num_survivors++
if(survivor.mind)
if(is_revolutionary(survivor))
num_revs++
if(num_survivors)
result += "Command's Approval Rating: <B>[100 - round((num_revs/num_survivors)*100, 0.1)]%</B><br>"
var/list/targets = list()
var/list/datum/mind/headrevs
var/list/datum/mind/revs
@@ -346,10 +468,22 @@
revs = ex_revs
else
revs = get_antag_minds(/datum/antagonist/rev, TRUE)
var/num_revs = 0
var/num_survivors = 0
for(var/mob/living/carbon/survivor in GLOB.alive_mob_list)
if(survivor.ckey)
num_survivors += 1
if ((survivor.mind in revs) || (survivor.mind in headrevs))
num_revs += 1
if(num_survivors)
result += "Command's Approval Rating: <B>[100 - round((num_revs/num_survivors)*100, 0.1)]%</B><br>"
if(headrevs.len)
var/list/headrev_part = list()
headrev_part += "<span class='header'>The head revolutionaries were:</span>"
headrev_part += printplayerlist(headrevs,TRUE)
headrev_part += printplayerlist(headrevs, !check_rev_victory())
result += headrev_part.Join("<br>")
if(revs.len)
@@ -408,3 +542,6 @@
/datum/team/revolution/is_gamemode_hero()
return SSticker.mode.name == "revolution"
#undef DECONVERTER_STATION_WIN
#undef DECONVERTER_REVS_WIN
@@ -3,7 +3,6 @@
typepath = /datum/round_event/ghost_role/slaughter
weight = 1 //Very rare
max_occurrences = 1
gamemode_blacklist = list("dynamic")
earliest_start = 1 HOURS
min_players = 20
@@ -5,6 +5,7 @@
max_occurrences = 1 //Only once okay fam
earliest_start = 30 MINUTES
min_players = 35
dynamic_should_hijack = TRUE
/datum/round_event/spawn_swarmer
@@ -28,17 +28,11 @@
/datum/traitor_class/human/forge_single_objective(datum/antagonist/traitor/T)
.=1
var/assassin_prob = 50
var/is_dynamic = FALSE
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
is_dynamic = TRUE
assassin_prob = max(0,mode.threat_level-20)
if(prob(assassin_prob))
if(is_dynamic)
var/threat_spent = CONFIG_GET(number/dynamic_assassinate_cost)
mode.spend_threat(threat_spent)
mode.log_threat("[T.owner.name] added [threat_spent] on an assassination target.")
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
@@ -46,17 +46,13 @@
traitor_kind.on_process(src)
/proc/get_random_traitor_kind(var/list/blacklist = list())
var/chaos_weight = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
chaos_weight = (mode.threat - 50)/50
var/list/weights = list()
for(var/C in GLOB.traitor_classes)
if(!(C in blacklist))
var/datum/traitor_class/class = GLOB.traitor_classes[C]
if(class.min_players > length(GLOB.joined_player_list))
continue
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,chaos_weight,class.chaos,0)
var/weight = LOGISTIC_FUNCTION(1.5*class.weight,0,class.chaos,0)
weights[C] = weight * 1000
var/choice = pickweight(weights, 0)
if(!choice)
@@ -50,6 +50,14 @@
A.death()
return ..()
/obj/item/soulstone/proc/hot_potato(mob/living/user)
to_chat(user, span_userdanger("Holy magics residing in \the [src] burn your hand!"))
var/obj/item/bodypart/affecting = user.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
affecting.receive_damage( 0, 10 ) // 10 burn damage
user.emote("scream")
user.update_damage_overlays()
user.dropItemToGround(src)
//////////////////////////////Capturing////////////////////////////////////////////////////////
/obj/item/soulstone/attack(mob/living/carbon/human/M, mob/living/user)
@@ -94,6 +102,35 @@
to_chat(A, "<b>You have been released from your prison, but you are still bound to the cult's will. Help them succeed in their goals at all costs.</b>")
was_used()
/obj/item/soulstone/pre_attack(atom/A, mob/living/user, params)
var/mob/living/simple_animal/hostile/construct/shade/occupant = (locate() in src)
var/obj/item/storage/toolbox/mechanical/target_toolbox = A
if(!occupant || !istype(target_toolbox) || target_toolbox.has_soul)
return ..()
if(iscultist(user))
hot_potato(user)
return
if(!iscultist(user, TRUE) && !iswizard(user) && !usability)
user.Unconscious(10 SECONDS)
to_chat(user, span_userdanger("Your body is wracked with debilitating pain!"))
return
user.visible_message("<span class='notice'>[user] holds [src] above [user.p_their()] head and forces it into [target_toolbox] with a flash of light!", \
span_notice("You hold [src] above your head briefly, then force it into [target_toolbox], transferring the [occupant]'s soul!"), ignored_mobs = occupant)
to_chat(occupant, span_userdanger("[user] holds you up briefly, then forces you into [target_toolbox]!"))
to_chat(occupant, span_deadsay("<b>Your eternal soul has been sacrificed to restore the soul of a toolbox. Them's the breaks!</b>"))
occupant.client?.give_award(/datum/award/achievement/misc/toolbox_soul, occupant)
occupant.deathmessage = "shrieks out in unholy pain as [occupant.p_their()] soul is absorbed into [target_toolbox]!"
release_shades(user, TRUE)
occupant.death()
target_toolbox.name = "soulful toolbox"
target_toolbox.icon_state = "toolbox_blue_old"
target_toolbox.has_soul = TRUE
target_toolbox.has_latches = FALSE
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
/obj/structure/constructshell
name = "empty shell"
+3
View File
@@ -145,6 +145,9 @@
else
to_chat(src,"<span class='userlove'>You can't make that genital [picked_organ.aroused_state ? "unaroused" : "aroused"]!</span>")
picked_organ.update_appearance()
if(ishuman(src))
var/mob/living/carbon/human/human = src
human.update_genitals()
return
+78
View File
@@ -11,6 +11,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset
var/_abstract = /datum/asset
var/cached_url_mappings
/// Whether or not this asset should be loaded in the "early assets" SS
var/early = FALSE
/datum/asset/New()
GLOB.asset_datums[type] = src
@@ -19,6 +23,13 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/proc/get_url_mappings()
return list()
/// Returns a cached tgui message of URL mappings
/datum/asset/proc/get_serialized_url_mappings()
if (isnull(cached_url_mappings))
cached_url_mappings = TGUI_CREATE_MESSAGE("asset/mappings", get_url_mappings())
return cached_url_mappings
/datum/asset/proc/register()
return
@@ -169,6 +180,8 @@ GLOBAL_LIST_EMPTY(asset_datums)
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
//any sprite modifications we want to do (aka, coloring a greyscaled asset)
I = ModifyInserted(I)
var/size_id = "[I.Width()]x[I.Height()]"
var/size = sizes[size_id]
@@ -185,6 +198,15 @@ GLOBAL_LIST_EMPTY(asset_datums)
sizes[size_id] = size = list(1, I, null)
sprites[sprite_name] = list(size_id, 0)
/**
* A simple proc handing the Icon for you to modify before it gets turned into an asset.
*
* Arguments:
* * I: icon being turned into an asset
*/
/datum/asset/spritesheet/proc/ModifyInserted(icon/pre_asset)
return pre_asset
/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
if (length(prefix))
prefix = "[prefix]-"
@@ -217,6 +239,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/size_id = sprite[SPR_SIZE]
return {"[name][size_id] [sprite_name]"}
/**
* Returns the size class (ex design32x32) for a given sprite's icon
*
* Arguments:
* * sprite_name - The sprite to get the size of
*/
/datum/asset/spritesheet/proc/icon_size_id(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return "[name][size_id]"
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
@@ -224,6 +259,24 @@ GLOBAL_LIST_EMPTY(asset_datums)
#undef SPRSZ_STRIPPED
/datum/asset/changelog_item
_abstract = /datum/asset/changelog_item
var/item_filename
/datum/asset/changelog_item/New(date)
item_filename = sanitize_filename("[date].yml")
SSassets.transport.register_asset(item_filename, file("html/changelogs/archive/" + item_filename))
/datum/asset/changelog_item/send(client)
if (!item_filename)
return
. = SSassets.transport.send_assets(client, item_filename)
/datum/asset/changelog_item/get_url_mappings()
if (!item_filename)
return
. = list("[item_filename]" = SSassets.transport.get_asset_url(item_filename))
/datum/asset/spritesheet/simple
_abstract = /datum/asset/spritesheet/simple
var/list/assets
@@ -315,3 +368,28 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/namespaced/proc/get_htmlloader(filename)
return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename]))
/// A subtype to generate a JSON file from a list
/datum/asset/json
_abstract = /datum/asset/json
/// The filename, will be suffixed with ".json"
var/name
/datum/asset/json/send(client)
return SSassets.transport.send_assets(client, "data/[name].json")
/datum/asset/json/get_url_mappings()
return list(
"[name].json" = SSassets.transport.get_asset_url("data/[name].json"),
)
/datum/asset/json/register()
var/filename = "data/[name].json"
fdel(filename)
text2file(json_encode(generate()), filename)
SSassets.transport.register_asset(filename, fcopy_rsc(filename))
fdel(filename)
/// Returns the data that will be JSON encoded
/datum/asset/json/proc/generate()
SHOULD_CALL_PARENT(FALSE)
CRASH("generate() not implemented for [type]!")
@@ -14,6 +14,12 @@
var/high_alert_category = "too_much_oxy"
var/high_alert_datum = /atom/movable/screen/alert/too_much_oxy
/datum/breathing_class/proc/get_effective_pp(datum/gas_mixture/breath)
var/mol = 0
for(var/gas in gases)
mol += breath.get_moles(gas) * gases[gas]
return (mol/breath.total_moles()) * breath.return_pressure()
/datum/breathing_class/oxygen
gases = list(
GAS_O2 = 1,
@@ -235,7 +235,7 @@
. += "can-open"
if(connected_port)
. += "can-connector"
var/pressure = air_contents.return_pressure()
var/pressure = air_contents?.return_pressure()
if(pressure >= 40 * ONE_ATMOSPHERE)
. += "can-o3"
else if(pressure >= 10 * ONE_ATMOSPHERE)
@@ -295,6 +295,7 @@
density = FALSE
playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
investigate_log("was destroyed.", INVESTIGATE_ATMOS)
update_icon_state()
if(holding)
holding.forceMove(T)
-5
View File
@@ -211,11 +211,6 @@
unit_name = "arrow"
export_types = list(/obj/item/ammo_casing/caseless/arrow, /obj/item/ammo_casing/caseless/arrow/bone, /obj/item/ammo_casing/caseless/arrow/ash)
/datum/export/weapon/bow_teaching
cost = 500
unit_name = "bowyery tablet"
export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow)
/datum/export/weapon/quiver
cost = 100
unit_name = "quiver"
+3
View File
@@ -128,6 +128,9 @@
return data
/obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
return
switch(action)
if("LZCargo")
usingBeacon = FALSE
+23
View File
@@ -183,6 +183,29 @@
var/item = pick(contains)
new item(C)
/datum/supply_pack/security/armory/frontier
name = "Old West Surplus Crate"
desc = "Do you have a fauna problem? Do you want to live out your frontier-taming fantasies on a state-of-the-art plasma research and mining station? Do you want to pretend you lived in the Old West of Earthen fame? Well, this box - and its many hats - may be for you."
cost = 7500
contraband = TRUE
contains = list(/obj/effect/spawner/bundle/crate/cowboyhat,
/obj/effect/spawner/bundle/crate/cowboyboots,
/obj/item/clothing/under/pants/chaps,
/obj/item/reagent_containers/food/snacks/butterbiscuit,
/obj/item/reagent_containers/food/snacks/butterbiscuit,
/obj/item/reagent_containers/food/snacks/plumphelmetbiscuit,
/obj/item/reagent_containers/food/snacks/plumphelmetbiscuit,
/obj/item/ammo_box/c38/pouch,
/obj/item/ammo_box/c38/pouch,
/obj/item/ammo_box/c38/pouch,
/obj/effect/spawner/bundle/crate/levergun
)
/datum/supply_pack/security/armory/frontier/fill(obj/structure/closet/crate/C)
for(var/i in 1 to 5)
var/item = pick(contains)
new item(C)
/datum/supply_pack/security/armory/swat
name = "SWAT Crate"
desc = "Contains two fullbody sets of tough, fireproof, pressurized suits designed in a joint effort by IS-ERI and Nanotrasen. Each set contains a suit, helmet, mask, combat belt, and combat gloves. Requires Armory access to open."
+3
View File
@@ -187,3 +187,6 @@
var/list/block_parry_hinted = list()
/// moused over objects, currently capped at 7. this is awful, and should be replaced with a component to track it using signals for parrying at some point.
var/list/moused_over_objects = list()
/// AFK tracking
var/last_activity = 0
+7 -1
View File
@@ -83,6 +83,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
return
last_activity = world.time
//Logs all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
@@ -222,6 +224,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
///////////
/client/New(TopicData)
last_activity = world.time
world.SetConfig("APP/admin", ckey, "role=admin")
var/tdata = TopicData //save this for later use
TopicData = null //Prevent calls to client.Topic from connect
@@ -578,9 +581,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
//If we aren't an admin, and the flag is set
if(CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey] && !(ckey in GLOB.bunker_passthrough))
var/living_recs = CONFIG_GET(number/panic_bunker_living)
var/vpn_living_recs = CONFIG_GET(number/panic_bunker_living_vpn)
//Relies on pref existing, but this proc is only called after that occurs, so we're fine.
var/minutes = get_exp_living(pure_numeric = TRUE)
if(minutes <= living_recs) // && !CONFIG_GET(flag/panic_bunker_interview)
if((minutes <= living_recs) || (IsVPN() && (minutes < vpn_living_recs)))
var/reject_message = "Failed Login: [key] - Account attempting to connect during panic bunker, but they do not have the required living time [minutes]/[living_recs]"
log_access(reject_message)
message_admins("<span class='adminnotice'>[reject_message]</span>")
@@ -850,6 +854,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info)
if(last_click > world.time - world.tick_lag)
return
last_activity = world.time
last_click = world.time
var/ab = FALSE
var/list/L = params2list(params)
@@ -922,6 +927,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
//checks if a client is afk
//3000 frames = 5 minutes
/client/proc/is_afk(duration = CONFIG_GET(number/inactivity_period))
var/inactivity = world.time - last_activity
if(inactivity > duration)
return inactivity
return FALSE
+3
View File
@@ -0,0 +1,3 @@
/client/proc/IsVPN()
var/datum/ipintel/res = get_ip_intel(address)
return res.intel >= CONFIG_GET(number/ipintel_rating_bad)
+1 -1
View File
@@ -15,6 +15,6 @@
var/indelay = stripped_input(usr, "Enter the tooltip delay in milliseconds (default: 500)", "Enter tooltip delay", "", 10)
indelay = text2num(indelay)
if(usr)//is this what you mean?
prefs.tip_delay = indelay
prefs.tip_delay = max(indelay, 0.01)
prefs.save_preferences()
to_chat(usr, "<span class='danger'>Tooltip delay set to [indelay] milliseconds.</span>")
+20 -3
View File
@@ -27,9 +27,12 @@
. = ..()
if(!ishuman(user))
return
if(slot == ITEM_SLOT_GLOVES)
var/mob/living/carbon/human/H = user
tackler = H.AddComponent(/datum/component/tackler, stamina_cost=tackle_stam_cost, base_knockdown = base_knockdown, range = tackle_range, speed = tackle_speed, skill_mod = skill_mod, min_distance = min_distance)
switch(slot) // I didn't like how it looked
if(SLOT_GLOVES)
var/mob/living/carbon/human/H = user
tackler = H.AddComponent(/datum/component/tackler, stamina_cost=tackle_stam_cost, base_knockdown = base_knockdown, range = tackle_range, speed = tackle_speed, skill_mod = skill_mod, min_distance = min_distance)
else
qdel(tackler) // Only wearing it!
/obj/item/clothing/gloves/tackler/dropped(mob/user)
. = ..()
@@ -67,6 +70,20 @@
resistance_flags = NONE
strip_mod = 1.2 // because apparently black gloves had this
/obj/item/clothing/gloves/tackler/combat/goliath
name = "goliath gloves"
desc = "Rudimentary tackling gloves. The goliath hide makes it great for grappling with targets, while also being fireproof."
icon = 'icons/obj/mining.dmi'
icon_state = "goligloves"
item_state = "goligloves"
tackle_stam_cost = 25
base_knockdown = 1 SECONDS
tackle_range = 5
tackle_speed = 2
min_distance = 2
skill_mod = 1
/obj/item/clothing/gloves/tackler/combat/insulated
name = "guerrilla gloves"
desc = "Superior quality combative gloves, good for performing tackle takedowns as well as absorbing electrical shocks."
+30 -7
View File
@@ -427,25 +427,30 @@
desc = "Comfy and supposedly flammable."
icon_state = "flannel"
item_state = "flannel"
/obj/item/clothing/suit/jacket/flannel/red
/obj/item/clothing/suit/toggle/jacket/flannel/red
name = "red flannel jacket"
desc = "Comfy and supposedly flammable."
icon_state = "flannel_red"
item_state = "flannel_red"
/obj/item/clothing/suit/jacket/flannel/aqua
togglename = "buttons"
/obj/item/clothing/suit/toggle/jacket/flannel/aqua
name = "aqua flannel jacket"
desc = "Comfy and supposedly flammable."
icon_state = "flannel_aqua"
item_state = "flannel_aqua"
/obj/item/clothing/suit/jacket/flannel/brown
togglename = "buttons"
/obj/item/clothing/suit/toggle/jacket/flannel/brown
name = "brown flannel jacket"
desc = "Comfy and supposedly flammable."
icon_state = "flannel_brown"
item_state = "flannel_brown"
togglename = "buttons"
/obj/item/clothing/suit/toggle/jacket/whitehoodie
name = "soft hoodie"
desc = "A soft hoodie with a TailorCo brand on the tag."
icon_state = "white_hoodie"
item_state = "white_hoodie"
togglename = "zipper"
/obj/item/clothing/suit/jacket/purplehoodie
name = "purple hoodie"
desc = "A soft purple hoodie with a TailorCo brand on the tag."
@@ -482,6 +487,24 @@
icon_state = "gothic_shirtcross"
item_state = "gothic_shirtcross"
/obj/item/clothing/suit/jacket/gentlecoat
name = "grey jacket"
desc = "A grey coat with a TailorCo brand on the tag. Looks expensive."
icon_state = "gentlecoat"
item_state = "gentlecoat"
/obj/item/clothing/suit/toggle/jacket/greenjacket
name = "green outdoorsmans jacket"
desc = "A green jacket with a TailorCo brand on the tag. Looks expensive."
icon_state = "coatar"
item_state = "coatar"
togglename = "zipper"
/obj/item/clothing/suit/toggle/jacket/fancytrench
name = "grey trenchcoat"
desc = "A custom-tailored trenchcoat with a TailorCo brand on the tag."
icon_state = "fancytrench"
item_state = "fancytrench"
togglename = "buttons"
/obj/item/clothing/suit/jacket/leather
name = "leather jacket"
desc = "Pompadour not included."
+17 -2
View File
@@ -1,3 +1,5 @@
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 10
//this datum is used by the events controller to dictate how it selects events
/datum/round_event_control
var/name //The human-readable name of the event
@@ -27,6 +29,9 @@
var/triggering //admin cancellation
/// Whether or not dynamic should hijack this event
var/dynamic_should_hijack = FALSE
/datum/round_event_control/New()
if(config && !wizardevent) // Magic is unaffected by configs
earliest_start = CEILING(earliest_start * CONFIG_GET(number/events_min_time_mul), 1)
@@ -53,6 +58,11 @@
return FALSE
if(holidayID && (!SSevents.holidays || !SSevents.holidays[holidayID]))
return FALSE
var/datum/game_mode/dynamic/dynamic = SSticker.mode
if (istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING)
return FALSE
return TRUE
/datum/round_event_control/wizard/canSpawnEvent(var/players_amt, var/gamemode)
@@ -62,14 +72,19 @@
return can_be_midround_wizard && ..()
return ..()
/datum/round_event_control/proc/preRunEvent()
if(!ispath(typepath, /datum/round_event))
return EVENT_CANT_RUN
if (SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_RANDOM_EVENT, src) & CANCEL_PRE_RANDOM_EVENT)
return EVENT_INTERRUPTED
triggering = TRUE
if (alert_observers)
message_admins("Random Event triggering in 30 seconds: [name] (<a href='?src=[REF(src)];cancel=1'>CANCEL</a>)")
sleep(300)
message_admins("Random Event triggering in [RANDOM_EVENT_ADMIN_INTERVENTION_TIME] seconds: [name] (<a href='?src=[REF(src)];cancel=1'>CANCEL</a>)")
sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME SECONDS)
var/gamemode = SSticker.mode.config_tag
var/players_amt = get_active_player_count(alive_check = TRUE, afk_check = TRUE, human_check = TRUE)
if(!canSpawnEvent(players_amt, gamemode))
+1 -1
View File
@@ -4,7 +4,7 @@
weight = 10
max_occurrences = 1
min_players = 30
gamemode_blacklist = list("nuclear","wizard","revolution","dynamic")
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/abductor
minimum_required = 2
+1 -1
View File
@@ -2,9 +2,9 @@
name = "Alien Infestation"
typepath = /datum/round_event/ghost_role/alien_infestation
weight = 5
gamemode_blacklist = list("dynamic")
min_players = 25
max_occurrences = 1
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/alien_infestation
announceWhen = 400
+1 -2
View File
@@ -6,8 +6,7 @@
earliest_start = 40 MINUTES
min_players = 35
gamemode_blacklist = list("blob","dynamic") //Just in case a blob survives that long
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/blob
announceWhen = -1
-1
View File
@@ -4,7 +4,6 @@
max_occurrences = 1
min_players = 20
earliest_start = 30 MINUTES //deadchat sink, lets not even consider it early on.
gamemode_blacklist = list("nuclear")
/datum/round_event/ghost_role/fugitives
minimum_required = 1
+1 -1
View File
@@ -3,7 +3,7 @@
typepath = /datum/round_event/ghost_role/nightmare
max_occurrences = 1
min_players = 20
gamemode_blacklist = list("dynamic")
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/nightmare
minimum_required = 1
+25 -38
View File
@@ -5,7 +5,7 @@
max_occurrences = 1
min_players = 10
earliest_start = 30 MINUTES
gamemode_blacklist = list("nuclear")
dynamic_should_hijack = TRUE
#define PIRATES_ROGUES "Rogues"
// #define PIRATES_SILVERSCALES "Silverscales"
@@ -17,19 +17,18 @@
return ..()
/datum/round_event/pirates
startWhen = 60 //2 minutes to answer
var/datum/comm_message/threat_msg
/datum/round_event/pirates/start()
send_pirate_threat()
/proc/send_pirate_threat()
var/pirate_type = PIRATES_ROGUES //pick(PIRATES_ROGUES, PIRATES_SILVERSCALES, PIRATES_DUTCHMAN)
var/datum/comm_message/threat_msg = new
var/payoff = 0
var/payoff_min = 1000
var/paid_off = FALSE
var/pirate_type
var/ship_template
var/ship_name = "Space Privateers Association"
var/shuttle_spawned = FALSE
/datum/round_event/pirates/setup()
pirate_type = PIRATES_ROGUES //pick(PIRATES_ROGUES, PIRATES_SILVERSCALES, PIRATES_DUTCHMAN)
var/initial_send_time = world.time
var/response_max_time = 2 MINUTES
switch(pirate_type)
if(PIRATES_ROGUES)
ship_name = pick(strings(PIRATE_NAMES_FILE, "rogue_names"))
@@ -38,11 +37,7 @@
// if(PIRATES_DUTCHMAN)
// ship_name = "Flying Dutchman"
/datum/round_event/pirates/announce(fake)
priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", "commandreport")
if(fake)
return
threat_msg = new
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
payoff = max(payoff_min, FLOOR(D.account_balance * 0.80, 1000))
@@ -62,35 +57,27 @@
// threat_msg.title = "Business proposition"
// threat_msg.content = "Ahoy! This be the [ship_name]. Cough up [payoff] credits or you'll walk the plank."
// threat_msg.possible_answers = list("We'll pay.","We will not be extorted.")
threat_msg.answer_callback = CALLBACK(src,.proc/answered)
threat_msg.answer_callback = CALLBACK(GLOBAL_PROC, .proc/pirates_answered, threat_msg, payoff, ship_name, initial_send_time, response_max_time, ship_template)
addtimer(CALLBACK(GLOBAL_PROC, .proc/spawn_pirates, threat_msg, ship_template, FALSE), response_max_time)
SScommunications.send_message(threat_msg,unique = TRUE)
/datum/round_event/pirates/proc/answered()
if(threat_msg?.answered == 1)
/proc/pirates_answered(datum/comm_message/threat_msg, payoff, ship_name, initial_send_time, response_max_time, ship_template)
if(world.time > initial_send_time + response_max_time)
priority_announce("Too late to beg for mercy!",sender_override = ship_name)
return
if(threat_msg && threat_msg.answered == 1)
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
if(D.adjust_money(-payoff))
priority_announce("Thanks for the credits, landlubbers.",sender_override = ship_name)
paid_off = TRUE
return
else
priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
else if(threat_msg?.answered == 2)
priority_announce("You won't pay? Fine then, we'll take those credits by force!",sender_override = ship_name)
if(!shuttle_spawned)
spawn_shuttle()
else
priority_announce("Too late to beg for mercy!",sender_override = ship_name)
spawn_pirates(threat_msg, ship_template, TRUE)
/datum/round_event/pirates/start()
if(threat_msg && !threat_msg.answered)
threat_msg.possible_answers = list("Too late")
threat_msg.answered = 1
if(!paid_off && !shuttle_spawned)
spawn_shuttle()
/datum/round_event/pirates/proc/spawn_shuttle()
shuttle_spawned = TRUE
/proc/spawn_pirates(datum/comm_message/threat_msg, ship_template, skip_answer_check)
if(!skip_answer_check && threat_msg?.answered == 1)
return
var/list/candidates = pollGhostCandidates("Do you wish to be considered for pirate crew?", ROLE_TRAITOR)
shuffle_inplace(candidates)
@@ -109,12 +96,12 @@
for(var/turf/A in ship.get_affected_turfs(T))
for(var/obj/effect/mob_spawn/human/pirate/spawner in A)
if(candidates.len > 0)
var/mob/M = candidates[1]
spawner.create(M.ckey)
candidates -= M
announce_to_ghosts(M)
var/mob/our_candidate = candidates[1]
spawner.create(our_candidate.ckey)
candidates -= our_candidate
notify_ghosts("The pirate ship has an object of interest: [our_candidate]!", source=our_candidate, action=NOTIFY_ORBIT, header="Something's Interesting!")
else
announce_to_ghosts(spawner)
notify_ghosts("The pirate ship has an object of interest: [spawner]!", source=spawner, action=NOTIFY_ORBIT, header="Something's Interesting!")
priority_announce("Unidentified armed ship detected near the station.")
+1
View File
@@ -4,6 +4,7 @@
weight = 8
max_occurrences = 1
min_players = 20
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/space_dragon
minimum_required = 1
+1
View File
@@ -5,6 +5,7 @@
weight = 10
earliest_start = 20 MINUTES
min_players = 15
dynamic_should_hijack = TRUE
/datum/round_event/ghost_role/space_ninja
minimum_required = 1
@@ -84,6 +84,12 @@
user.visible_message("<span class='suicide'>[user] is scratching [user.p_their()] back as hard as [user.p_they()] can with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (BRUTELOSS)
/obj/item/cultivator/bone
name = "bone cultivator"
desc = "A handle and a few bones tied together to resemble a hoe. Should work for removing weeds."
icon = 'icons/obj/mining.dmi'
icon_state = "cultivator_bone"
/obj/item/hatchet
name = "hatchet"
desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood."
@@ -112,6 +118,12 @@
playsound(src, 'sound/weapons/bladeslice.ogg', 50, 1, -1)
return (BRUTELOSS)
/obj/item/hatchet/bone
name = "bone hatchet"
desc = "A primitive hatchet made out of mostly bone, with some sinew to keep it together. It just might do for cutting logs into planks."
icon = 'icons/obj/mining.dmi'
icon_state = "hatchet_bone"
/obj/item/scythe
icon_state = "scythe0"
lefthand_file = 'icons/mob/inhands/weapons/polearms_lefthand.dmi'
@@ -7,11 +7,11 @@
instrument_flags = INSTRUMENT_LEGACY
volume_multiplier = 1 //not as loud as synth'd
/datum/instrument/hardcoded/accordian
name = "Accordian"
id = "accordian"
/datum/instrument/hardcoded/accordion
name = "Accordion"
id = "accordion"
legacy_instrument_ext = "mid"
legacy_instrument_path = "accordian"
legacy_instrument_path = "accordion"
/datum/instrument/hardcoded/bikehorn
name = "Bike Horn"
@@ -19,8 +19,8 @@
"60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
"72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
/datum/instrument/organ/crisis_accordian
name = "Crisis Accordian"
/datum/instrument/organ/crisis_accordion
name = "Crisis Accordion"
id = "crack"
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
"48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
@@ -34,8 +34,8 @@
"60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
"72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
/datum/instrument/organ/crisis_tango_accordian
name = "Crisis Tango Accordian"
/datum/instrument/organ/crisis_tango_accordion
name = "Crisis Tango Accordion"
id = "crtango"
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
"48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
+3
View File
@@ -63,6 +63,9 @@
var/list/mind_traits // Traits added to the mind of the mob assigned this job
var/list/blacklisted_quirks //list of quirk typepaths blacklisted.
/// Should this job be allowed to be picked for the bureaucratic error event?
var/allow_bureaucratic_error = TRUE
var/display_order = JOB_DISPLAY_ORDER_DEFAULT
//If a job complies with dresscodes, loadout items will not be equipped instead of the job's outfit, instead placing the items into the player's backpack.
+1
View File
@@ -14,6 +14,7 @@
exp_type = EXP_TYPE_CREW
exp_type_department = EXP_TYPE_SILICON
display_order = JOB_DISPLAY_ORDER_AI
allow_bureaucratic_error = FALSE
var/do_special_check = TRUE
threat = 5
considered_combat_role = TRUE
+1 -1
View File
@@ -5,7 +5,7 @@
department_flag = CIVILIAN
faction = "Station"
total_positions = 0
spawn_positions = 2
spawn_positions = 4
supervisors = "the security team"
outfit = /datum/outfit/job/prisoner
@@ -6,6 +6,7 @@
set hidden = TRUE
client_keysend_amount += 1
last_activity = world.time
var/cache = client_keysend_amount
@@ -89,6 +90,7 @@
set hidden = TRUE
keys_held -= _key
last_activity = world.time
var/movement = movement_keys[_key]
if(!(next_move_dir_add & movement))
next_move_dir_sub |= movement
+4 -2
View File
@@ -312,12 +312,14 @@ Key procs
/datum/language/machine = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
/datum/language/slime = list(LANGUAGE_ATOM),
/datum/language/dwarf = list(LANGUAGE_ATOM))
/datum/language/dwarf = list(LANGUAGE_ATOM),
/datum/language/neokanji = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/machine = list(LANGUAGE_ATOM),
/datum/language/draconic = list(LANGUAGE_ATOM),
/datum/language/slime = list(LANGUAGE_ATOM),
/datum/language/dwarf = list(LANGUAGE_ATOM))
/datum/language/dwarf = list(LANGUAGE_ATOM),
/datum/language/neokanji = list(LANGUAGE_ATOM))
/datum/language_holder/venus
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
+25
View File
@@ -0,0 +1,25 @@
/datum/language/neokanji
name = "Neo-Kanji"
desc = "A bastardized mixture of many old Earth asian dialects. Famously known as the official language of the spider clan."
speech_verb = "proclaims"
ask_verb = "queries"
exclaim_verb = "declares"
whisper_verb = "hushes"
key = "k"
space_chance = 40
default_priority = 94
flags = TONGUELESS_SPEECH
chooseable_roundstart = TRUE
syllables = list("ka", "ki", "ku", "ke", "ko", "ta",
"chi", "tsu", "te", "to", "sa", "shi",
"su", "se", "so", "na","ni","nu","ne",
"no","n","ha","hi","fu","he","ho","ma",
"mi","mu","me","mo","ya","yu","yo","ra",
"ri","ru","re","ro","wa","wo", "an", "ang",
"ao", "ba", "bai", "ban", "bang", "bao",
"bei", "beng", "chuai", "xing", "xong", "zhao",
"zhong", "xil", "ping", "dang", "guang", "guan",
"jing", "jiao", "kung", "fu", "lo", "wang",
"liu", "ling", "mang", "nong", "peng", "qiong",
"san","tiao", "wan","xiong", "men")
icon_state = "neokanji"
+3
View File
@@ -232,6 +232,9 @@
return data
/obj/structure/chisel_message/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
return
var/mob/user = usr
var/is_admin = check_rights_for(user.client, R_ADMIN)
var/is_creator = user.ckey == creator_key
@@ -228,6 +228,13 @@
/obj/item/kinetic_crusher/glaive/update_icon_state()
item_state = "crusher[wielded]-glaive" // this is not icon_state and not supported by 2hcomponent
/obj/item/kinetic_crusher/glaive/bone
name = "necropolis bone glaive"
desc = "Tribals trying to immitate technology have spent a long time to somehow assemble bits and pieces to work together just like the real thing. \
Although it does take a lot of effort and luck to create, it was a success."
icon_state = "crusher-bone"
item_state = "crusher0-bone"
//destablizing force
/obj/item/projectile/destabilizer
name = "destabilizing force"
@@ -179,6 +179,12 @@
custom_materials = list(/datum/material/iron=50)
w_class = WEIGHT_CLASS_SMALL
/obj/item/shovel/spade/bone
name = "bone spade"
desc = "A bone spade, suitable for digging and moving dirt."
icon_state = "spade_bone"
toolspeed = 0.75
/obj/item/shovel/serrated
name = "serrated bone shovel"
desc = "A wicked tool that cleaves through dirt just as easily as it does flesh. The design was styled after ancient lavaland tribal designs."
+1 -1
View File
@@ -13,7 +13,7 @@ INITIALIZE_IMMEDIATE(/mob/dead)
stack_trace("Warning: [src]([type]) initialized multiple times!")
flags_1 |= INITIALIZED_1
tag = "mob_[next_mob_id++]"
GLOB.mob_list += src
add_to_mob_list()
prepare_huds()
@@ -33,6 +33,13 @@
. = ..()
GLOB.new_player_list += src
/mob/dead/new_player/Destroy()
GLOB.new_player_list -= src
return ..()
/mob/dead/new_player/prepare_huds()
return
@@ -391,7 +398,7 @@
var/id_max = text2num(href_list["maxid"])
if( (id_max - id_min) > 100 ) //Basic exploit prevention
//(protip, this stops no exploits)
//(protip, this stops no exploits)
to_chat(usr, "The option ID difference is too big. Please contact administration or the database admin.")
return
+1 -1
View File
@@ -123,7 +123,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
animate(src, pixel_y = 2, time = 10, loop = -1)
GLOB.dead_mob_list += src
add_to_dead_mob_list()
for(var/v in GLOB.active_alternate_appearances)
if(!v)
+4 -4
View File
@@ -64,8 +64,8 @@
brainmob.container = src
if(!(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death.
brainmob.stat = CONSCIOUS //we manually revive the brain mob
GLOB.dead_mob_list -= brainmob
GLOB.alive_mob_list += brainmob
brainmob.remove_from_dead_mob_list()
brainmob.add_to_alive_mob_list()
brainmob.reset_perspective()
brain = newbrain
@@ -102,8 +102,8 @@
brainmob.stat = DEAD
brainmob.emp_damage = 0
brainmob.reset_perspective() //so the brainmob follows the brain organ instead of the mmi. And to update our vision
GLOB.alive_mob_list -= brainmob //Get outta here
GLOB.dead_mob_list += brainmob
brainmob.remove_from_alive_mob_list() //Get outta here
brainmob.add_to_dead_mob_list()
brain.brainmob = brainmob //Set the brain to use the brainmob
brainmob = null //Set mmi brainmob var to null
if(user)
-3
View File
@@ -25,9 +25,6 @@
else
emp_damage = max(emp_damage-1, 0)
/mob/living/brain/handle_status_effects()
return
/mob/living/brain/handle_traits()
return
+2 -2
View File
@@ -159,8 +159,8 @@ GLOBAL_VAR(posibrain_notify_cooldown)
to_chat(brainmob, welcome_message)
brainmob.mind.assigned_role = new_role
brainmob.stat = CONSCIOUS
GLOB.dead_mob_list -= brainmob
GLOB.alive_mob_list += brainmob
brainmob.remove_from_dead_mob_list()
brainmob.add_to_alive_mob_list()
visible_message(new_mob_message)
check_success()
@@ -22,7 +22,7 @@
/mob/living/carbon/Moved()
. = ..()
if(. && (movement_type & FLOATING)) //floating is easy
if(. && !CHECK_BITFIELD(movement_type, FLOATING)) //floating is easy
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
set_nutrition(NUTRITION_LEVEL_FED - 1) //just less than feeling vigorous
else if(nutrition && stat != DEAD)
+30 -3
View File
@@ -83,6 +83,33 @@
. += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
. += "Absorbed DNA: [changeling.absorbedcount]"
//NINJACODE
if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
. += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]"
. += "Current Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]"
if(SN.s_initialized)
//Suit gear
. += "Energy Charge: [round(SN.cell.charge/100)]%"
//Ninja status
. += "Fingerprints: [md5(dna.uni_identity)]"
. += "Unique Identity: [dna.unique_enzymes]"
. += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]"
. += "Nutrition Status: [nutrition]"
. += "Oxygen Loss: [getOxyLoss()]"
. += "Toxin Levels: [getToxLoss()]"
. += "Burn Severity: [getFireLoss()]"
. += "Brute Trauma: [getBruteLoss()]"
. += "Radiation Levels: [radiation] rad"
. += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)"
//Diseases
if(length(diseases))
. += "Viruses:"
for(var/thing in diseases)
var/datum/disease/D = thing
. += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]"
/mob/living/carbon/human/show_inv(mob/user)
user.set_machine(src)
var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask)
@@ -1028,10 +1055,10 @@
return
if(!HAS_TRAIT(src, TRAIT_IGNOREDAMAGESLOWDOWN)) //if we want to ignore slowdown from damage, but not from equipment
var/scaling = maxHealth / 100
var/health_deficiency = max(((maxHealth / scaling) - (health / scaling)), (getStaminaLoss()*0.75))
var/health_deficiency = max(((maxHealth / scaling) - (health / scaling)), max(0, getStaminaLoss() - 39))
if(health_deficiency >= 40)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, (health_deficiency - 15) / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, (health_deficiency - 15) / 25)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown, TRUE, health_deficiency / 75)
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying, TRUE, health_deficiency / 25)
else
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown)
remove_movespeed_modifier(/datum/movespeed_modifier/damage_slowdown_flying)
@@ -1397,7 +1397,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
////////////
/datum/species/proc/handle_digestion(mob/living/carbon/human/H)
if(HAS_TRAIT(src, TRAIT_NOHUNGER))
if(HAS_TRAIT(H, TRAIT_NOHUNGER))
return //hunger is for BABIES
//The fucking TRAIT_FAT mutation is the dumbest shit ever. It makes the code so difficult to work with
@@ -14,5 +14,6 @@
skinned_type = /obj/item/stack/sheet/animalhide/xeno
exotic_bloodtype = "X*"
damage_overlay_type = "xeno"
liked_food = MEAT
disliked_food = JUNKFOOD
liked_food = GROSS | MEAT
species_category = SPECIES_CATEGORY_ALIEN
+4 -13
View File
@@ -570,24 +570,15 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
else
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "jittery")
if(stuttering)
stuttering = max(stuttering-1, 0)
if(druggy)
adjust_drugginess(-1)
if(slurring || drunkenness)
slurring = max(slurring-1,0,drunkenness)
if(cultslurring)
cultslurring = max(cultslurring-1, 0)
if(clockcultslurring)
clockcultslurring = max(clockcultslurring-1, 0)
if(drunkenness)
drunkenness = max(drunkenness-1,0)
if(silent)
silent = max(silent-1, 0)
if(druggy)
adjust_drugginess(-1)
if(hallucination)
handle_hallucinations()
+2 -2
View File
@@ -65,9 +65,9 @@
I.on_mob_death(src, gibbed)
if(mind)
mind.store_memory("Time of death: [tod]", 0)
GLOB.alive_mob_list -= src
remove_from_alive_mob_list()
if(!gibbed)
GLOB.dead_mob_list += src
add_to_dead_mob_list()
if(ckey)
var/datum/preferences/P = GLOB.preferences_datums[ckey]
if(P)
+16 -1
View File
@@ -150,11 +150,26 @@
/mob/living/proc/handle_stomach()
return
//this updates all special effects: knockdown, druggy, stuttering, etc..
/*
* this updates some effects: mostly old stuff such as drunkness, druggy, stuttering, etc.
* that should be converted to status effect datums one day.
*/
/mob/living/proc/handle_status_effects()
if(confused)
confused = max(0, confused - 1)
if(stuttering)
stuttering = max(stuttering-1, 0)
if(slurring)
slurring = max(slurring-1,0)
if(cultslurring)
cultslurring = max(cultslurring-1, 0)
if(clockcultslurring)
clockcultslurring = max(clockcultslurring-1, 0)
/mob/living/proc/handle_traits()
//Eyes
if(eye_blind) //blindness, heals slowly over time
+6 -6
View File
@@ -589,8 +589,8 @@
if(full_heal)
fully_heal(admin_revive)
if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain)
GLOB.dead_mob_list -= src
GLOB.alive_mob_list += src
remove_from_dead_mob_list()
add_to_alive_mob_list()
suiciding = 0
stat = UNCONSCIOUS //the mob starts unconscious,
if(!eye_blind)
@@ -1289,11 +1289,11 @@
return FALSE
if(NAMEOF(src, stat))
if((stat == DEAD) && (var_value < DEAD))//Bringing the dead back to life
GLOB.dead_mob_list -= src
GLOB.alive_mob_list += src
remove_from_dead_mob_list()
add_to_alive_mob_list()
if((stat < DEAD) && (var_value == DEAD))//Kill he
GLOB.alive_mob_list -= src
GLOB.dead_mob_list += src
remove_from_alive_mob_list()
add_to_dead_mob_list()
if(NAMEOF(src, health)) //this doesn't work. gotta use procs instead.
return FALSE
. = ..()
@@ -166,7 +166,7 @@ GLOBAL_LIST_EMPTY(block_parry_data)
/// Clickdelay duration post-parry if you fail to parry an attack
var/parry_failed_clickcd_duration = 0 SECONDS
/// Parry cooldown post-parry if failed. This is ADDED to parry_cooldown!!!
var/parry_failed_cooldown_duration = 0 SECONDS
var/parry_failed_cooldown_duration = 3.5 SECONDS
// Advanced
/// Flags added to return value
+1 -1
View File
@@ -7,6 +7,6 @@
clear_fullscreens()
//New pAI's get a brand new mind to prevent meta stuff from their previous life. This new mind causes problems down the line if it's not deleted here.
GLOB.alive_mob_list -= src
remove_from_alive_mob_list()
ghostize()
qdel(src)
@@ -1,5 +1,3 @@
#define PAI_EMP_SILENCE_DURATION 3 MINUTES
/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
return FALSE
@@ -9,7 +7,7 @@
return
take_holo_damage(severity/2)
DefaultCombatKnockdown(severity*4)
silent = max(silent, (PAI_EMP_SILENCE_DURATION) / SSmobs.wait / severity)
short_radio()
if(holoform)
fold_in(force = TRUE)
emitter_next_use = world.time + emitter_emp_cd
@@ -95,8 +95,8 @@
if(mmi.brainmob)
if(mmi.brainmob.stat == DEAD)
mmi.brainmob.stat = CONSCIOUS
GLOB.dead_mob_list -= mmi.brainmob
GLOB.alive_mob_list += mmi.brainmob
mmi.brainmob.remove_from_dead_mob_list()
mmi.brainmob.add_to_alive_mob_list()
mind.transfer_to(mmi.brainmob)
mmi.update_icon()
else
@@ -66,6 +66,7 @@ GLOBAL_LIST(bad_gremlin_items)
/mob/living/simple_animal/hostile/gremlin/Initialize()
. = ..()
AddElement(/datum/element/ventcrawling, given_tier = VENTCRAWLER_ALWAYS)
ADD_TRAIT(src, TRAIT_SHOCKIMMUNE, INNATE_TRAIT)
access_card = new /obj/item/card/id(src)
var/datum/job/captain/C = new /datum/job/captain
access_card.access = C.get_access()
@@ -95,20 +95,78 @@
/mob/living/simple_animal/hostile/carp/cayenne
name = "Cayenne"
real_name = "Cayenne"
desc = "A failed Syndicate experiment in weaponized space carp technology, it now serves as a lovable mascot."
gender = FEMALE
regen_amount = 8
speak_emote = list("squeaks")
maxHealth = 90
health = 90
gold_core_spawnable = NO_SPAWN
faction = list(ROLE_SYNDICATE, "carp") //They are still a carp
AIStatus = AI_OFF
gold_core_spawnable = NO_SPAWN
faction = list(ROLE_SYNDICATE)
/// Keeping track of the nuke disk for the functionality of storing it.
var/obj/item/disk/nuclear/disky
/// Location of the file storing disk overlays
// var/icon/disk_overlay_file = 'icons/mob/carp.dmi'
/// Colored disk mouth appearance for adding it as a mouth overlay
var/mutable_appearance/colored_disk_mouth
harm_intent_damage = 12
obj_damage = 70
melee_damage_lower = 15
melee_damage_upper = 18
/mob/living/simple_animal/hostile/carp/cayenne/Initialize()
. = ..()
// AddElement(/datum/element/pet_bonus, "bloops happily!")
// colored_disk_mouth = mutable_appearance(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/carp/disk_mouth, greyscale_colors), "disk_mouth")
ADD_TRAIT(src, TRAIT_DISK_VERIFIER, INNATE_TRAIT) //carp can verify disky
/mob/living/simple_animal/hostile/carp/cayenne/IsAdvancedToolUser()
return TRUE //carp SMART
/mob/living/simple_animal/hostile/carp/cayenne/death(gibbed)
if(disky)
disky.forceMove(drop_location())
disky = null
return ..()
/mob/living/simple_animal/hostile/carp/cayenne/Destroy(force)
QDEL_NULL(disky)
return ..()
/mob/living/simple_animal/hostile/carp/cayenne/examine(mob/user)
. = ..()
if(disky)
. += span_notice("Wait... is that [disky] in [p_their()] mouth?")
/mob/living/simple_animal/hostile/carp/cayenne/AttackingTarget(atom/attacked_target)
if(istype(attacked_target, /obj/item/disk/nuclear))
var/obj/item/disk/nuclear/potential_disky = attacked_target
if(potential_disky.anchored)
return
potential_disky.forceMove(src)
disky = potential_disky
to_chat(src, span_nicegreen("YES!! You manage to pick up [disky]. (Click anywhere to place it back down.)"))
update_icon()
if(!disky.fake)
client.give_award(/datum/award/achievement/misc/cayenne_disk, src)
return
if(disky)
if(isopenturf(attacked_target))
to_chat(src, span_notice("You place [disky] on [attacked_target]"))
disky.forceMove(attacked_target.drop_location())
disky = null
update_icon()
else
disky.melee_attack_chain(src, attacked_target)
return
return ..()
/mob/living/simple_animal/hostile/carp/cayenne/Exited(atom/movable/gone, direction)
. = ..()
if(disky == gone)
disky = null
update_icon()
/mob/living/simple_animal/hostile/carp/cayenne/update_overlays()
. = ..()
if(!disky || stat == DEAD)
return
// . += colored_disk_mouth
// . += mutable_appearance(disk_overlay_file, "disk_overlay")
#undef REGENERATION_DELAY
@@ -1,11 +1,8 @@
#define MINER_DASH_RANGE 4
/*
BLOOD-DRUNK MINER
Effectively a highly aggressive miner, the blood-drunk miner has very few attacks but compensates by being highly aggressive.
The blood-drunk miner's attacks are as follows
- If not in KA range, it will rapidly dash at its target
- If in KA range, it will fire its kinetic accelerator
@@ -13,11 +10,8 @@ The blood-drunk miner's attacks are as follows
- After any of these attacks, may transform its cleaving saw:
Untransformed, it attacks very rapidly for smaller amounts of damage
Transformed, it attacks at normal speed for higher damage and cleaves enemies hit
When the blood-drunk miner dies, it leaves behind the cleaving saw it was using and its kinetic accelerator.
Difficulty: Medium
*/
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner
@@ -37,7 +31,7 @@ Difficulty: Medium
move_to_delay = 2
projectiletype = /obj/item/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
ranged = 1
ranged = TRUE
ranged_cooldown_time = 16
pixel_x = -16
crusher_loot = list(/obj/item/melee/transforming/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator/premiumka, /obj/item/crusher_trophy/miner_eye, /obj/item/disk/design_disk/modkit_disc/mob_and_turf_aoe,
@@ -55,18 +49,56 @@ Difficulty: Medium
var/dashing = FALSE
var/dash_cooldown = 15
var/guidance = FALSE
var/transform_stop_attack = FALSE // stops the blood drunk miner from attacking after transforming his weapon until the next attack chain
deathmessage = "falls to the ground, decaying into glowing particles."
death_sound = "bodyfall"
deathsound = "bodyfall"
footstep_type = FOOTSTEP_MOB_HEAVY
attack_action_types = list(/datum/action/innate/megafauna_attack/dash,
/datum/action/innate/megafauna_attack/kinetic_accelerator,
/datum/action/innate/megafauna_attack/transform_weapon)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
miner_saw = new(src)
/datum/action/innate/megafauna_attack/dash
name = "Dash To Target"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now dashing to your target.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/kinetic_accelerator
name = "Fire Kinetic Accelerator"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
chosen_message = "<span class='colossus'>You are now shooting your kinetic accelerator.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/transform_weapon
name = "Transform Weapon"
icon_icon = 'icons/obj/lavaland/artefacts.dmi'
button_icon_state = "cleaving_saw"
chosen_message = "<span class='colossus'>You are now transforming your weapon.</span>"
chosen_attack_num = 3
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
if(client)
switch(chosen_attack)
if(1)
dash(target)
if(2)
shoot_ka()
if(3)
transform_weapon()
return
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
dash_attack()
else
shoot_ka()
transform_weapon()
/obj/item/melee/transforming/cleaving_saw/miner //nerfed saw because it is very murdery
force = 6
@@ -74,7 +106,7 @@ Difficulty: Medium
/obj/item/melee/transforming/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user)
target.add_stun_absorption("miner", 10, INFINITY)
. = ..()
..()
target.stun_absorption -= "miner"
/obj/item/projectile/kinetic/miner
@@ -83,22 +115,16 @@ Difficulty: Medium
icon_state = "ka_tracer"
range = MINER_DASH_RANGE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize()
. = ..()
internal = new/obj/item/gps/internal/miner(src)
miner_saw = new(src)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
var/adjustment_amount = amount * 0.1
if(world.time + adjustment_amount > next_action)
DelayNextAction(adjustment_amount, considered_action = FALSE, flush = TRUE) //attacking it interrupts it attacking, but only briefly
DelayNextAction(adjustment_amount) //attacking it interrupts it attacking, but only briefly
. = ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/death()
if(health > 0)
return
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
return ..()
. = ..()
if(.)
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
@@ -110,11 +136,14 @@ Difficulty: Medium
return
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/MeleeAction(patience = TRUE)
transform_stop_attack = FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
if(QDELETED(target))
return
if(!CheckActionCooldown() || !Adjacent(target)) //some cheating
INVOKE_ASYNC(src, .proc/quick_attack_loop)
if(client)
transform_stop_attack = FALSE
if(QDELETED(target) || transform_stop_attack)
return
face_atom(target)
if(isliving(target))
@@ -129,12 +158,10 @@ Difficulty: Medium
adjustHealth(-(L.maxHealth * 0.5))
L.gib()
return TRUE
miner_saw.melee_attack_chain(src, target, null, ATTACK_IGNORE_CLICKDELAY)
FlushCurrentAction()
DelayNextAction(CLICK_CD_MELEE)
miner_saw.melee_attack_chain(src, target)
if(guidance)
adjustHealth(-2)
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
@@ -147,16 +174,10 @@ Difficulty: Medium
. = ..()
if(. && target && !targets_the_same)
wander = TRUE
transform_weapon()
INVOKE_ASYNC(src, .proc/quick_attack_loop)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
INVOKE_ASYNC(src, .proc/dash, target)
else
shoot_ka()
transform_weapon()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash_attack()
INVOKE_ASYNC(src, .proc/dash, target)
shoot_ka()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
@@ -165,22 +186,7 @@ Difficulty: Medium
face_atom(target)
new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir)
Shoot(target)
DelayNextAction(CLICK_CD_RANGE, flush = TRUE)
//I'm still of the belief that this entire proc needs to be wiped from existence.
// do not take my touching of it to be endorsement of it. ~mso
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/quick_attack_loop()
while(!QDELETED(target) && !CheckActionCooldown()) //this is done this way because next_move can change to be sooner while we sleep.
stoplag(1)
sleep((next_action - world.time) * 1.5) //but don't ask me what the fuck this is about
if(QDELETED(target))
return
if(dashing || !CheckActionCooldown() || !Adjacent(target))
if(dashing && next_action <= world.time)
SetNextAction(1, considered_action = FALSE, immediate = FALSE, flush = TRUE)
INVOKE_ASYNC(src, .proc/quick_attack_loop) //lets try that again.
return
AttackingTarget()
DelayNextAction(CLICK_CD_RANGE)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
if(world.time < dash_cooldown)
@@ -225,19 +231,23 @@ Difficulty: Medium
dashing = TRUE
alpha = 0
animate(src, alpha = 255, time = 5)
sleep(2)
SLEEP_CHECK_DEATH(2)
D.forceMove(step_forward_turf)
forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, 1, -1)
sleep(1)
SLEEP_CHECK_DEATH(1)
dashing = FALSE
shoot_ka()
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
if(time_until_next_transform <= world.time)
miner_saw.transform_cooldown = 0
miner_saw.transform_weapon(src, TRUE)
if(!miner_saw.active)
rapid_melee = 5 // 4 deci cooldown before changes, npcpool subsystem wait is 20, 20/4 = 5
else
rapid_melee = 3 // same thing but halved (slightly rounded up)
transform_stop_attack = TRUE
icon_state = "miner[miner_saw.active ? "_transformed":""]"
icon_living = "miner[miner_saw.active ? "_transformed":""]"
time_until_next_transform = world.time + rand(50, 100)
@@ -269,6 +279,14 @@ Difficulty: Medium
desc = "The sweet blood, oh, it sings to me."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/guidance
guidance = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/doom
name = "hostile-environment miner"
desc = "A miner destined to hop across dimensions for all eternity, hunting anomalous creatures."
@@ -277,4 +295,5 @@ Difficulty: Medium
ranged_cooldown_time = 8
dash_cooldown = 8
#undef MINER_DASH_RANGE
@@ -364,7 +364,7 @@ Difficulty: Normal
var/turf/T = get_turf(victim)
if(!istype(victim) || victim.stat == DEAD || !T || arena_cooldown > world.time)
return
if((istype(get_area(T), /area/ruin/unpowered/hierophant) || istype(get_area(src), /area/ruin/unpowered/hierophant)) && victim != src)
if((istype(get_area(T), /area/ruin/lavaland/unpowered/hierophant) || istype(get_area(src), /area/ruin/lavaland/unpowered/hierophant)) && victim != src)
return
arena_cooldown = world.time + initial(arena_cooldown)
for(var/d in GLOB.cardinals)
@@ -32,6 +32,9 @@ Difficulty: Hard
wander = FALSE
del_on_death = TRUE
blood_volume = BLOOD_VOLUME_NORMAL
achievement_type = /datum/award/achievement/boss/wendigo_kill
crusher_achievement_type = /datum/award/achievement/boss/wendigo_crusher
score_achievement_type = /datum/award/score/wendigo_score
deathmessage = "falls, shaking the ground around it"
deathsound = 'sound/effects/gravhit.ogg'
attack_action_types = list(/datum/action/innate/megafauna_attack/heavy_stomp,
@@ -136,7 +136,7 @@ GLOBAL_LIST_EMPTY(plague_rats)
if(LAZYLEN(GLOB.plague_rats) >= cap)
visible_message("<span class='warning'>[src] gnaws into its food, [cap] rats are now on the station!</span>")
return
var/mob/living/newmouse = new /mob/living/simple_animal/hostile/plaguerat(loc)
new /mob/living/simple_animal/hostile/plaguerat(loc)
visible_message("<span class='notice'>[src] gnaws into its food, attracting another rat!</span>")
/mob/living/simple_animal/hostile/plaguerat/proc/exit_vents()
@@ -456,7 +456,7 @@
var/dir_to_target = get_dir(get_turf(src), get_turf(L))
var/throwtarget = get_edge_target_turf(target, dir_to_target)
L.safe_throw_at(throwtarget, 10, 1, src)
L.Paralyze(50)
L.drop_all_held_items()
addtimer(CALLBACK(src, .proc/reset_status), 4 + ((tiredness * tiredness_mult) / 10))
tiredness = tiredness + (gust_tiredness * tiredness_mult)
@@ -195,12 +195,6 @@
stat = CONSCIOUS
med_hud_set_status()
/mob/living/simple_animal/handle_status_effects()
..()
if(stuttering)
stuttering = 0
/mob/living/simple_animal/proc/handle_automated_action()
set waitfor = FALSE
return
+1 -1
View File
@@ -1,5 +1,5 @@
/mob/Login()
GLOB.player_list |= src
add_to_player_list()
lastKnownIP = client.address
computer_id = client.computer_id
log_access("Mob Login: [key_name(src)] was assigned to a [type]")
+1 -1
View File
@@ -3,7 +3,7 @@
log_message("[key_name(src)] is no longer owning mob [src]([src.type])", LOG_OWNERSHIP)
SStgui.on_logout(src)
unset_machine()
GLOB.player_list -= src
remove_from_player_list()
..()
+6 -8
View File
@@ -1,9 +1,8 @@
/mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game.
GLOB.mob_list -= src
GLOB.dead_mob_list -= src
GLOB.alive_mob_list -= src
remove_from_mob_list()
remove_from_dead_mob_list()
remove_from_alive_mob_list()
GLOB.all_clockwork_mobs -= src
GLOB.mob_directory -= tag
focus = null
LAssailant = null
movespeed_modification = null
@@ -22,12 +21,11 @@
return QDEL_HINT_HARDDEL
/mob/Initialize()
GLOB.mob_list += src
GLOB.mob_directory[tag] = src
add_to_mob_list()
if(stat == DEAD)
GLOB.dead_mob_list += src
add_to_dead_mob_list()
else
GLOB.alive_mob_list += src
add_to_alive_mob_list()
set_focus(src)
prepare_huds()
for(var/v in GLOB.active_alternate_appearances)
+3
View File
@@ -180,3 +180,6 @@
///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
/// A mock client, provided by tests and friends
var/datum/client_interface/mock_client
+119
View File
@@ -0,0 +1,119 @@
///Adds the mob reference to the list and directory of all mobs. Called on Initialize().
/mob/proc/add_to_mob_list()
GLOB.mob_list |= src
GLOB.mob_directory[tag] = src
///Removes the mob reference from the list and directory of all mobs. Called on Destroy().
/mob/proc/remove_from_mob_list()
GLOB.mob_list -= src
GLOB.mob_directory -= tag
///Adds the mob reference to the list of all mobs alive. If mob is cliented, it adds it to the list of all living player-mobs.
/mob/proc/add_to_alive_mob_list()
GLOB.alive_mob_list |= src
if(client)
add_to_current_living_players()
///Removes the mob reference from the list of all mobs alive. If mob is cliented, it removes it from the list of all living player-mobs.
/mob/proc/remove_from_alive_mob_list()
GLOB.alive_mob_list -= src
if(client)
remove_from_current_living_players()
///Adds the mob reference to the list of all the dead mobs. If mob is cliented, it adds it to the list of all dead player-mobs.
/mob/proc/add_to_dead_mob_list()
GLOB.dead_mob_list |= src
if(client)
add_to_current_dead_players()
///Remvoes the mob reference from list of all the dead mobs. If mob is cliented, it adds it to the list of all dead player-mobs.
/mob/proc/remove_from_dead_mob_list()
GLOB.dead_mob_list -= src
if(client)
remove_from_current_dead_players()
///Adds the cliented mob reference to the list of all player-mobs, besides to either the of dead or alive player-mob lists, as appropriate. Called on Login().
/mob/proc/add_to_player_list()
SHOULD_CALL_PARENT(TRUE)
GLOB.player_list |= src
if(!SSticker?.mode)
return
if(stat == DEAD)
add_to_current_dead_players()
else
add_to_current_living_players()
///Removes the mob reference from the list of all player-mobs, besides from either the of dead or alive player-mob lists, as appropriate. Called on Logout().
/mob/proc/remove_from_player_list()
SHOULD_CALL_PARENT(TRUE)
GLOB.player_list -= src
if(!SSticker?.mode)
return
if(stat == DEAD)
remove_from_current_dead_players()
else
remove_from_current_living_players()
///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game.
/mob/proc/add_to_current_dead_players()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_DEAD_PLAYERS] |= src
/mob/dead/observer/add_to_current_dead_players()
if(!SSticker?.mode)
return
if(started_as_observer)
SSticker.mode.current_players[CURRENT_OBSERVERS] |= src
return
return ..()
/mob/dead/new_player/add_to_current_dead_players()
return
///Removes the mob reference from either the list of dead player-mobs or from the list of observers, depending on how they joined the game.
/mob/proc/remove_from_current_dead_players()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_DEAD_PLAYERS] -= src
/mob/dead/observer/remove_from_current_dead_players()
if(!SSticker?.mode)
return
if(started_as_observer)
SSticker.mode.current_players[CURRENT_OBSERVERS] -= src
return
return ..()
///Adds the cliented mob reference to the list of living player-mobs. If the mob is an antag, it adds it to the list of living antag player-mobs.
/mob/proc/add_to_current_living_players()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_LIVING_PLAYERS] |= src
if(mind && (mind.special_role || length(mind.antag_datums)))
add_to_current_living_antags()
///Removes the mob reference from the list of living player-mobs. If the mob is an antag, it removes it from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_players()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_LIVING_PLAYERS] -= src
if(LAZYLEN(mind?.antag_datums))
remove_from_current_living_antags()
///Adds the cliented mob reference to the list of living antag player-mobs.
/mob/proc/add_to_current_living_antags()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_LIVING_ANTAGS] |= src
///Removes the mob reference from the list of living antag player-mobs.
/mob/proc/remove_from_current_living_antags()
if(!SSticker?.mode)
return
SSticker.mode.current_players[CURRENT_LIVING_ANTAGS] -= src
+10
View File
@@ -4,6 +4,7 @@
set name = "say_indicator"
set hidden = TRUE
set category = "IC"
client?.last_activity = world.time
display_typing_indicator()
var/message = input(usr, "", "say") as text|null
// If they don't type anything just drop the message.
@@ -21,12 +22,16 @@
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
clear_typing_indicator() // clear it immediately!
client?.last_activity = world.time
say(message)
/mob/verb/me_typing_indicator()
set name = "me_indicator"
set hidden = TRUE
set category = "IC"
client?.last_activity = world.time
display_typing_indicator()
var/message = input(usr, "", "me") as message|null
// If they don't type anything just drop the message.
@@ -52,6 +57,8 @@
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
clear_typing_indicator() // clear it immediately!
client?.last_activity = world.time
usr.emote("me",1,message,TRUE)
/mob/say_mod(input, message_mode)
@@ -73,6 +80,7 @@
return lowertext(copytext_char(input, 1, customsayverb))
/mob/proc/whisper_keybind()
client?.last_activity = world.time
var/message = input(src, "", "whisper") as text|null
if(!length(message))
return
@@ -89,6 +97,7 @@
whisper(message)
/mob/proc/whisper(message, datum/language/language=null)
client?.last_activity = world.time
say(message, language) //only living mobs actually whisper, everything else just talks
/mob/proc/say_dead(var/message)
@@ -132,6 +141,7 @@
message = emoji_parse(message)
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name] <span class='message'>[emoji_parse(spanned)]</span></span>"
log_talk(message, LOG_SAY, tag="DEAD")
client?.last_activity = world.time
deadchat_broadcast(rendered, follow_target = src, speaker_key = key)
/mob/proc/check_emote(message)
-1
View File
@@ -15,7 +15,6 @@
//Defines for the suit's unique abilities
#define IS_NINJA_SUIT_INITIALIZATION(action) (istype(action, /datum/action/item_action/initialize_ninja_suit))
#define IS_NINJA_SUIT_STATUS(action) (istype(action, /datum/action/item_action/ninjastatus))
#define IS_NINJA_SUIT_BOOST(action) (istype(action, /datum/action/item_action/ninjaboost))
#define IS_NINJA_SUIT_EMP(action) (istype(action, /datum/action/item_action/ninjapulse))
#define IS_NINJA_SUIT_STAR_CREATION(action) (istype(action, /datum/action/item_action/ninjastar))
+3
View File
@@ -21,3 +21,6 @@
var/obj/item/clothing/suit/space/space_ninja/S = H.wear_suit
if(istype(H.belt, belt))
S.energyKatana = H.belt
H.grant_language(/datum/language/neokanji)
var/datum/language_holder/LH = H.get_language_holder()
LH.selected_language = /datum/language/neokanji
@@ -5,35 +5,3 @@
button_icon_state = "health"
icon_icon = 'icons/obj/device.dmi'
var/action_background_icon_state = "bg_default_on"
/**
* Proc called to put a status readout to the ninja in chat.
*
* Called put some information about the ninja's current status into chat.
* This information used to be displayed constantly on the status tab screen
* when the suit was on, but was turned into this as to remove the code from
* human.dm
*/
/obj/item/clothing/suit/space/space_ninja/proc/ninjastatus()
var/mob/living/carbon/human/ninja = affecting
var/list/info_list = list()
info_list += "<span class='info'>SpiderOS Status: [s_initialized ? "Initialized" : "Disabled"]</span>\n"
//Ninja status
info_list += "<span class='info'>Fingerprints: [md5(ninja.dna.uni_identity)]</span>\n"
info_list += "<span class='info'>Unique Identity: [ninja.dna.unique_enzymes]</span>\n"
info_list += "<span class='info'>Overall Status: [ninja.stat > 1 ? "dead" : "[ninja.health]% healthy"]</span>\n"
info_list += "<span class='info'>Nutrition Status: [ninja.nutrition]</span>\n"
info_list += "<span class='info'>Oxygen Loss: [ninja.getOxyLoss()]</span>\n"
info_list += "<span class='info'>Toxin Levels: [ninja.getToxLoss()]</span>\n"
info_list += "<span class='info'>Burn Severity: [ninja.getFireLoss()]</span>\n"
info_list += "<span class='info'>Brute Trauma: [ninja.getBruteLoss()]</span>\n"
info_list += "<span class='info'>Radiation Levels: [ninja.radiation] rad</span>\n"
info_list += "<span class='info'>Body Temperature: [ninja.bodytemperature-T0C] degrees C ([ninja.bodytemperature*1.8-459.67] degrees F)</span>\n"
//Diseases
if(length(ninja.diseases))
info_list += "Viruses:"
for(var/datum/disease/ninja_disease in ninja.diseases)
info_list += "<span class='info'>* [ninja_disease.name], Type: [ninja_disease.spread_text], Stage: [ninja_disease.stage]/[ninja_disease.max_stages], Possible Cure: [ninja_disease.cure_text]</span>\n"
to_chat(ninja, "[info_list.Join()]")
-3
View File
@@ -121,9 +121,6 @@
if(s_coold > 0)
to_chat(user, "<span class='warning'><b>ERROR</b>: suit is on cooldown.</span>")
return FALSE
if(IS_NINJA_SUIT_STATUS(action))
ninjastatus()
return TRUE
if(IS_NINJA_SUIT_BOOST(action))
ninjaboost()
return TRUE
@@ -39,7 +39,7 @@
/obj/item/ammo_casing/c38/lethal
name = ".38 bullet casing"
desc = "A .38 bullet casing"
desc = "A .38 bullet casing."
projectile_type = /obj/item/projectile/bullet/c38
/obj/item/ammo_casing/c38/trac
@@ -60,6 +60,13 @@
desc = "Designed to quickly reload revolvers. These rounds are manufactured within extremely tight tolerances, making them easy to show off trickshots with."
ammo_type = /obj/item/ammo_casing/c38/match
/obj/item/ammo_box/c38/pouch
name = "ammo packet (.38)"
desc = "A small ammunition packet made of synthleather. Not particularly useful for speedily reloading, but can hold a bit more."
multiload = FALSE
max_ammo = 8
icon_state = "38pch"
/obj/item/ammo_box/g4570
name = "ammo box (.45-70 GOVT)"
desc = "Brought to you at great expense,this box contains 10 more .45-70 GOVT bullets."
@@ -23,4 +23,10 @@
/obj/item/ammo_box/magazine/mm712x82/match
name = "box magazine (Match 7.12x82mm)"
icon_state = "a762-50"
ammo_type = /obj/item/ammo_casing/mm712x82/match
max_ammo = 50
/obj/item/ammo_box/magazine/mm712x82/update_icon()
..()
icon_state = "a762-[round(ammo_count(),10)]"
@@ -61,3 +61,7 @@
ammo_type = /obj/item/ammo_casing/c38 // they're rubber by default, i guess
caliber = "38"
max_ammo = 7
/obj/item/ammo_box/magazine/internal/shot/levergun/brush
name = "brush gun internal magazine"
ammo_type = /obj/item/ammo_casing/g4570
@@ -122,7 +122,7 @@
icon_state = "c20r[magazine ? "-[CEILING(get_ammo(0)/4, 1)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]"
/obj/item/gun/ballistic/automatic/wt550
name = "security semi-auto smg"
name = "security semi-auto PDW"
desc = "An outdated personal defence weapon. Uses 4.6x30mm rounds and is designated the WT-550 Semi-Automatic SMG."
icon_state = "wt550"
item_state = "arg"
@@ -11,6 +11,7 @@
mag_type = /obj/item/ammo_box/magazine/internal/shot
casing_ejector = FALSE
var/recentpump = 0 // to prevent spammage
var/clip_delay = CLICK_CD_MELEE
weapon_weight = WEAPON_HEAVY
sawn_item_state = "sawnshotgun"
@@ -24,6 +25,8 @@
playsound(user, 'sound/weapons/shotguninsert.ogg', 60, 1)
A.update_icon()
update_icon()
if(istype(A, /obj/item/ammo_box))
user.SetNextAction(clip_delay)
/obj/item/gun/ballistic/shotgun/process_chamber(mob/living/user, empty_chamber = 0)
return ..() //changed argument value
@@ -240,20 +243,20 @@
/obj/item/gun/ballistic/shotgun/automatic/combat/compact
name = "warden's combat shotgun"
desc = "A modified version of the semi automatic combat shotgun with a collapsible stock. For close encounters."
desc = "A modified version of the semi-automatic combat shotgun with a collapsible stock and a safety that prevents firing while folded. For close encounters."
icon_state = "cshotgunc"
mag_type = /obj/item/ammo_box/magazine/internal/shot/com
w_class = WEIGHT_CLASS_NORMAL
var/stock = FALSE
var/extend_sound = 'sound/weapons/batonextend.ogg'
recoil = 5
spread = 2
/obj/item/gun/ballistic/shotgun/automatic/combat/compact/AltClick(mob/living/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)) || item_flags & IN_STORAGE)
return
toggle_stock(user)
return TRUE
. = ..()
/obj/item/gun/ballistic/shotgun/automatic/combat/compact/examine(mob/user)
. = ..()
@@ -271,11 +274,20 @@
to_chat(user, "You fold the stock.")
recoil = 5
spread = 2
playsound(src.loc, extend_sound, 50, 1)
update_icon()
/obj/item/gun/ballistic/shotgun/automatic/combat/compact/update_icon_state()
icon_state = "[current_skin ? unique_reskin[current_skin] : "cshotgun"][stock ? "" : "c"]"
/obj/item/gun/ballistic/shotgun/automatic/combat/compact/afterattack(atom/target, mob/living/user, flag, params)
if(!stock)
shoot_with_empty_chamber(user)
to_chat(user, "<span class='warning'>[src] won't fire with a folded stock!</span>")
else
. = ..()
update_icon()
//Dual Feed Shotgun
/obj/item/gun/ballistic/shotgun/automatic/dual_tube
@@ -349,9 +361,13 @@
icon_state = "levercarabine"
item_state = "leveraction"
sawn_item_state = "maresleg"
var/can_cut = TRUE
/obj/item/gun/ballistic/shotgun/leveraction/attackby(obj/item/A, mob/user, params)
..()
if(!can_cut)
to_chat(user, "<span class='warning'>You can't cut \the [src] down!</span>")
return
if(A.tool_behaviour == TOOL_SAW || istype(A, /obj/item/gun/energy/plasmacutter))
sawoff(user)
if(istype(A, /obj/item/melee/transforming/energy))
@@ -367,3 +383,11 @@
icon_state = "[unique_reskin[current_skin]][sawn_off ? "-sawn" : ""][chambered ? "" : "-e"]"
else
icon_state = "[initial(icon_state)][sawn_off ? "-sawn" : ""][chambered ? "" : "-e"]"
/obj/item/gun/ballistic/shotgun/leveraction/brush
name = "brush gun"
desc = "While lever-actions have been horribly out of date for hundreds of years now, \
putting a nicely sized hole in a man-sized target with a .45-70 round has stayed relatively timeless."
icon_state = "brushgun"
can_cut = FALSE
mag_type = /obj/item/ammo_box/magazine/internal/shot/levergun/brush
@@ -278,6 +278,7 @@
w_class = WEIGHT_CLASS_SMALL
require_module = 1
module_type = list(/obj/item/robot_module/miner)
module_flags = BORG_MODULE_MINER
var/denied_type = null
var/maximum_of_type = 1
var/cost = 30
@@ -1,6 +1,6 @@
/obj/item/gun/magic/staff/motivation
name = "Motivation"
desc = "Rumored to have the ability to open up a portal the depths of Lavaland."
desc = "Rumored to have the ability to open up a portal to the depths of Lavaland."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "motivation"
item_state = "motivation"
@@ -21,15 +21,15 @@
recharge_rate = 5
var/datum/action/judgement_cut/judgementcut = new/datum/action/judgement_cut()
block_parry_data = /datum/block_parry_data/motivation
//to get this to toggle correctly
/obj/item/gun/magic/staff/motivation/Initialize()
. = ..()
judgementcut = new(src)
//lets the user know that their judgment cuts are recharging
//lets the user know that their judgement cuts are recharging
/obj/item/gun/magic/staff/motivation/shoot_with_empty_chamber(mob/living/user as mob|obj)
to_chat(user, "<span class='warning'>Judgment Cut is recharging.</span>")
to_chat(user, "<span class='warning'>Judgement Cut is recharging.</span>")
//action button to toggle judgement cuts on/off
/datum/action/judgement_cut
@@ -47,7 +47,7 @@
/obj/item/gun/magic/staff/motivation/can_trigger_gun(mob/living/user)
. = ..()
if(!judgementcut.judgement_toggled)
to_chat(user, "<span class='notice'> Judgment Cut is disabled.</span>")
to_chat(user, "<span class='notice'>Judgement Cut is disabled.</span>")
return FALSE
//adds/removes judgement cut and judgement cut end upon pickup/drop
@@ -84,6 +84,7 @@
else
reagents.trans_to(D, amount_per_transfer_from_this, 1/range)
D.add_atom_colour(mix_color_from_reagents(D.reagents.reagent_list), TEMPORARY_COLOUR_PRIORITY)
playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6)
last_spray = world.time
INVOKE_ASYNC(D, /obj/effect/decal/chempuff/proc/run_puff, A)
@@ -37,7 +37,7 @@
build_path = /obj/item/storage/bag/tray
category = list("initial","Dinnerware")
/datum/design/tray
/datum/design/cafeteria_tray
name = "Cafeteria Tray"
id = "foodtray"
build_type = AUTOLATHE
@@ -242,13 +242,24 @@
sleep(30)
playsound(C, 'sound/machines/defib_zap.ogg', 50, FALSE)
if(check_revivable())
var/tplus = world.time - C.timeofdeath
playsound(C, 'sound/machines/defib_success.ogg', 50, FALSE)
C.set_heartattack(FALSE)
var/oxydamage = C.getOxyLoss()
if(C.health < HEALTH_THRESHOLD_FULLCRIT && oxydamage)
var/diff = C.health - HEALTH_THRESHOLD_FULLCRIT
C.adjustOxyLoss(diff) //Heal their oxydamage up to hardcrit (or if less, as much as they have, since the proc has sanity)
C.revive(full_heal = FALSE, admin_revive = FALSE)
C.emote("gasp")
C.Jitter(100)
SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK)
log_game("[C] has been successfully defibrillated by nanites.")
var/list/policies = CONFIG_GET(keyed_list/policy)
var/timelimit = CONFIG_GET(number/defib_cmd_time_limit) * 10 //the config is in seconds, not deciseconds
var/late = timelimit && (tplus > timelimit)
var/policy = late? policies[POLICYCONFIG_ON_DEFIB_LATE] : policies[POLICYCONFIG_ON_DEFIB_INTACT]
if(policy)
to_chat(C, policy)
C.log_message("has been successfully defibrillated by nanites, [tplus] deciseconds from time of death, considered [late? "late" : "memory-intact"] revival under configured policy limits.", LOG_GAME)
else
playsound(C, 'sound/machines/defib_failed.ogg', 50, FALSE)
+2 -1
View File
@@ -528,7 +528,8 @@
BZ_brain_damage_min += bz
gas_max[GAS_N2] = PP(breath, GAS_N2) + 5
var/o2_pp = PP(breath, GAS_O2)
var/datum/breathing_class/class = GLOB.gas_data.breathing_classes[breathing_class]
var/o2_pp = class.get_effective_pp(breath)
safe_breath_min = min(3, 0.3 * o2_pp)
safe_breath_max = max(18, 1.3 * o2_pp + 1)
..()
+3 -1
View File
@@ -26,6 +26,7 @@
/datum/language/vampiric,
/datum/language/dwarf,
/datum/language/signlanguage,
/datum/language/neokanji,
))
healing_factor = STANDARD_ORGAN_HEALING*5 //Fast!!
decay_factor = STANDARD_ORGAN_DECAY/2
@@ -253,7 +254,8 @@
/datum/language/beachbum,
/datum/language/aphasia,
/datum/language/sylvan,
/datum/language/voltaic
/datum/language/voltaic,
/datum/language/neokanji,
))
/obj/item/organ/tongue/ethereal/Initialize(mapload)

Some files were not shown because too many files have changed in this diff Show More