Merge branch 'master' into familyport

This commit is contained in:
keronshb
2022-02-11 14:04:57 -05:00
1209 changed files with 276175 additions and 55667 deletions
+3 -3
View File
@@ -115,9 +115,9 @@ GLOBAL_PROTECT(LastAdminCalledProc)
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
return call("/proc/[procname]")(arglist(arguments))
else if(target != world)
return call(target, procname)(arglist(arguments))
return text2path("/proc/[procname]")? call("/proc/[procname]")(arglist(arguments)) : null
else if(target != world && istype(target, /datum)) // isdatum check incase someone manages to call WrapAdminProcCall(global) which would otherwise crash the process entirely
return hascall(target, procname)? call(target, procname)(arglist(arguments)) : null
else
log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
+1 -1
View File
@@ -55,7 +55,7 @@
"name" = initial(item.name),
"desc" = initial(item.desc),
// at this point initializing the item is probably faster tbh
"sprite" = icon2base64(icon(initial(item.icon), initial(item.icon_state))),
"sprite" = icon2base64(icon(initial(item.icon), initial(item.icon_state), SOUTH, 1)),
)
return data
+6
View File
@@ -2038,6 +2038,12 @@
var/obj/item/station_charter/charter = locate(href_list["reject_custom_name"])
if(istype(charter))
charter.reject_proposed(usr)
else if(href_list["approve_custom_name"])
if(!check_rights(R_ADMIN))
return
var/obj/item/station_charter/charter = locate(href_list["approve_custom_name"])
if(istype(charter))
charter.allow_pass(usr)
else if(href_list["jumpto"])
if(!isobserver(usr) && !check_rights(R_ADMIN))
return
+1 -1
View File
@@ -242,7 +242,7 @@
var/selectors_used = FALSE
var/list/combined_refs = list()
do
CHECK_TICK
stoplag(2)
finished = TRUE
for(var/i in running)
var/datum/SDQL2_query/query = i
@@ -3,11 +3,19 @@
/proc/_abs(A)
return abs(A)
/proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null)
var/mutable_appearance/MA = new()
for(var/v in set_vars)
MA.vars[v] = set_vars[v]
animate(A, appearance = MA, time, loop, easing, flags)
/proc/_animate(atom/A, list/data, time = 10, loop = 1, easing = LINEAR_EASING, flags = null)
if(!istype(A))
return
animate(A, appearance = data, time = time, loop = loop, easing = easing, flags = flags)
/proc/_animate_adv(atom/A, list/data, loop = 1, easing = LINEAR_EASING, flags = NONE)
if(!A || !islist(data) || data.len < 1)
return
animate(A, appearance = (data[1] - "time"), time = data[1]["time"], loop = loop, easing = easing, flags = flags)
if(data.len < 2)
return
for(var/i in 2 to data.len)
animate(appearance = (data[i] - "time"), time = data[i]["time"])
/proc/_acrccos(A)
return arccos(A)
+9 -6
View File
@@ -3,7 +3,7 @@
set category = "Object"
if((O.obj_flags & DANGEROUS_POSSESSION) && CONFIG_GET(flag/forbid_singulo_possession))
to_chat(usr, "[O] is too powerful for you to possess.")
to_chat(usr, "[O] is too powerful for you to possess.", confidential = TRUE)
return
var/turf/T = get_turf(O)
@@ -18,19 +18,22 @@
if(!usr.control_object) //If you're not already possessing something...
usr.name_archive = usr.real_name
usr.loc = O
usr.forceMove(O)
usr.real_name = O.name
usr.name = O.name
usr.reset_perspective(O)
usr.control_object = O
O.AddElement(/datum/element/weather_listener, /datum/weather/ash_storm, ZTRAIT_ASHSTORM, GLOB.ash_storm_sounds)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Possess Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/release()
set name = "Release Obj"
set category = "Object"
//usr.loc = get_turf(usr)
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
if(!usr.control_object) //lest we are banished to the nullspace realm.
return
if(usr.name_archive) //if you have a name archived
usr.real_name = usr.name_archive
usr.name_archive = ""
usr.name = usr.real_name
@@ -38,8 +41,8 @@
var/mob/living/carbon/human/H = usr
H.name = H.get_visible_name()
usr.loc = get_turf(usr.control_object)
usr.control_object.RemoveElement(/datum/element/weather_listener, /datum/weather/ash_storm, ZTRAIT_ASHSTORM, GLOB.ash_storm_sounds)
usr.forceMove(get_turf(usr.control_object))
usr.reset_perspective()
usr.control_object = null
SSblackbox.record_feedback("tally", "admin_verb", 1, "Release Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+1 -1
View File
@@ -1592,7 +1592,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
light_range = 2
duration = 9
/obj/effect/temp_visual/target/ex_act()
/obj/effect/temp_visual/target/ex_act(severity, target, origin)
return
/obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit)
@@ -79,7 +79,7 @@
. = TRUE
if("mass_apply")
if(!check_rights_for(usr.client, R_FUN))
to_chat(usr, "<span class='userdanger>Stay in your lane, jannie.</span>'")
to_chat(usr, span_userdanger("Stay in your lane, jannie."))
return
var/target_path = text2path(params["path"])
if(!target_path)
+187 -77
View File
@@ -1,20 +1,36 @@
GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist
///Public name for this antagonist. Appears for player prompts and round-end reports.
var/name = "Antagonist"
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
var/datum/mind/owner //Mind that owns this datum
var/silent = FALSE //Silent will prevent the gain/lose texts to show
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
var/delete_on_mind_deletion = TRUE
///Section of roundend report, datums with same category will be displayed together, also default header for the section
var/roundend_category = "other antagonists"
///Set to false to hide the antagonists from roundend report
var/show_in_roundend = TRUE
///If false, the roundtype will still convert with this antag active
var/prevent_roundtype_conversion = TRUE
///Mind that owns this datum
var/datum/mind/owner
///Silent will prevent the gain/lose texts to show
var/silent = FALSE
///Whether or not the person will be able to have more than one datum
var/can_coexist_with_others = TRUE
///List of datums this type can't coexist with
var/list/typecache_datum_blacklist = list()
///The define string we use to identify the role for bans/player polls to spawn a random new one in.
var/job_rank
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
///Should replace jobbanned player with ghosts if granted.
var/replace_banned = TRUE
///List of the objective datums that this role currently has, completing all objectives at round-end will cause this antagonist to greentext.
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
///String dialogue that is added to the player's in-round notes and memories regarding specifics of that antagonist, eg. the nuke code for nuke ops, or your unlock code for traitors.
var/antag_memory = ""
///typepath of moodlet that the mob will gain when granted this antagonist type.
var/antag_moodlet
///What is the configuration of this antagonist's hud icon, such as it's screen position and style, so thatit doesn't break other in-game hud icons.
var/antag_hud_type
///Name of the antag hud we provide to this mob.
var/antag_hud_name
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
var/hijack_speed = 0
@@ -22,25 +38,36 @@ GLOBAL_LIST_EMPTY(antagonists)
var/suicide_cry = ""
/// The typepath for the outfit to show in the preview for the preferences menu.
var/preview_outfit
/// If set to true, the antag will not be added to the living antag list.
var/soft_antag = FALSE
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/threat = 0 // Amount of threat this antag poses, for dynamic mode
var/show_to_ghosts = FALSE // Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
///This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/show_in_antagpanel = TRUE
///Antagpanel will display these together, REQUIRED
var/antagpanel_category = "Uncategorized"
///Will append antagonist name in admin listings - use for categories that share more than one antag type
var/show_name_in_check_antagonists = FALSE
/// Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
var/show_to_ghosts = FALSE
/* CIT SPECIFIC */
/// Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute)
/// Amount of threat this antag poses, for dynamic mode
var/threat = 0
var/delete_on_mind_deletion = TRUE
var/list/skill_modifiers
/* CIT SPECIFIC end */
//ANTAG UI
///name of the UI that will try to open, right now using a generic ui
var/ui_name = "AntagInfoGeneric"
///name of the UI that will try to open, right now having nothing means this won't exist but in the future all should.
var/ui_name
///button to access antag interface
var/datum/action/antag_info/info_button
//temporarily disable it for all antagonists other than families
var/ui_enable
/datum/antagonist/New()
GLOB.antagonists += src
@@ -48,7 +75,9 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/Destroy()
GLOB.antagonists -= src
if(owner)
if(!owner)
stack_trace("Destroy()ing antagonist datum when it has no owner.")
else
LAZYREMOVE(owner.antag_datums, src)
owner = null
return ..()
@@ -68,14 +97,14 @@ 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.
///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))
if(!soft_antag && old_body && old_body.stat != DEAD && !length(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
apply_innate_effects(new_body)
if(new_body.stat != DEAD)
if(!soft_antag && new_body.stat != DEAD)
new_body.add_to_current_living_antags()
//This handles the application of antag huds/special abilities
@@ -86,55 +115,76 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
return
/// This is called when the antagonist is being mindshielded.
/datum/antagonist/proc/pre_mindshield(mob/implanter, mob/living/mob_override)
SIGNAL_HANDLER
// return COMPONENT_MINDSHIELD_PASSED
/// This is called when the antagonist is successfully mindshielded.
/datum/antagonist/proc/on_mindshield(mob/implanter, mob/living/mob_override)
SIGNAL_HANDLER
return
// Adds the specified antag hud to the player. Usually called in an antag datum file
/datum/antagonist/proc/add_antag_hud(antag_hud_type, antag_hud_name, mob/living/mob_override)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.join_hud(mob_override)
set_antag_hud(mob_override, antag_hud_name)
// Removes the specified antag hud from the player. Usually called in an antag datum file
/datum/antagonist/proc/remove_antag_hud(antag_hud_type, mob/living/mob_override)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.leave_hud(mob_override)
set_antag_hud(mob_override, null)
// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
var/mob/living/carbon/human/H = mob_override
if(H && istype(H) && owner.assigned_role == "Clown")
if(removing) // They're a clown becoming an antag, remove clumsy
H.dna.remove_mutation(CLOWNMUT)
if(!silent && message)
to_chat(H, "<span class='boldnotice'>[message]</span>")
else
H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
if(!ishuman(mob_override) || owner.assigned_role != "Clown")
return
var/mob/living/carbon/human/human_override = mob_override
if(removing) // They're a clown becoming an antag, remove clumsy
human_override.dna.remove_mutation(CLOWNMUT)
if(!silent && message)
to_chat(human_override, span_boldnotice("[message]"))
else
human_override.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
///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
if(!owner)
CRASH("[src] ran on_gain() without a mind")
if(!owner.current)
CRASH("[src] ran on_gain() on a mind without a mob")
if(ui_name)//in the future, this should entirely replace greet.
if(ui_enable == TRUE)
info_button = new(owner.current, src)
info_button.Grant(owner.current)
info_button = new(owner.current, src)
info_button.Grant(owner.current)
if(!silent)
greet()
if(ui_name)
if(ui_enable == TRUE)
to_chat(owner.current, span_big("You are \a [src]."))
to_chat(owner.current, span_boldnotice("For more info, read the panel. you can always come back to it using the button in the top left."))
info_button.Trigger()
to_chat(owner.current, span_big("You are \a [src]."))
to_chat(owner.current, span_boldnotice("For more info, read the panel. you can always come back to it using the button in the top left."))
info_button.Trigger()
apply_innate_effects()
give_antag_moodies()
remove_blacklisted_quirks()
// RegisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT, .proc/pre_mindshield)
// RegisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED, .proc/on_mindshield)
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
// else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.toggles & DEADMIN_ANTAGONIST))
// owner.current.client.holder.auto_deadmin()
if(!soft_antag && owner.current.stat != DEAD)
owner.current.add_to_current_living_antags()
// cit skill
if(skill_modifiers)
for(var/A in skill_modifiers)
ADD_SINGLETON_SKILL_MODIFIER(owner, A, type)
@@ -142,63 +192,95 @@ 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)
/**
* Proc that checks the sent mob aganst the banlistfor this antagonist.
* Returns FALSE if no mob is sent, or the mob is not found to be banned.
*
* * mob/M: The mob that you are looking for on the banlist.
*/
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
. = (jobban_isbanned(M, ROLE_SYNDICATE) || QDELETED(M) || (job_rank && (jobban_isbanned(M,job_rank) || QDELETED(M))))
/**
* Proc that replaces a player who cannot play a specific antagonist due to being banned via a poll, and alerts the player of their being on the banlist.
*/
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
var/list/mob/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", job_rank, 50, owner.current)
if(LAZYLEN(candidates))
var/mob/C = pick(candidates)
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner)]) to replace a jobbanned player.")
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.
/**
* 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)
if(!owner)
CRASH("Antag datum with no owner.")
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(info_button)
QDEL_NULL(info_button)
if(!silent && owner.current)
farewell()
LAZYREMOVE(owner.antag_datums, src)
// cit skill
for(var/A in skill_modifiers)
owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
// end
if(!LAZYLEN(owner.antag_datums) && !soft_antag)
owner.current.remove_from_current_living_antags()
if(info_button)
QDEL_NULL(info_button)
if(!silent && owner.current)
farewell()
// UnregisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT)
// UnregisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED)
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
// we don't remove the activity component on purpose--no real point to it
qdel(src)
/**
* Proc that sends fluff or instructional messages to the player when they are given this antag datum.
* Use this proc for playing sounds, sending alerts, or helping to setup non-gameplay influencing aspects of the antagonist type.
*/
/datum/antagonist/proc/greet()
return
/**
* Proc that sends fluff or instructional messages to the player when they lose this antag datum.
* Use this proc for playing sounds, sending alerts, or otherwise informing the player that they're no longer a specific antagonist type.
*/
/datum/antagonist/proc/farewell()
return
/**
* Proc that assigns this antagonist's ascribed moodlet to the player.
*/
/datum/antagonist/proc/give_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "antag_moodlet", antag_moodlet)
/**
* Proc that removes this antagonist's ascribed moodlet from the player.
*/
/datum/antagonist/proc/clear_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
/**
* Removes invalid quirks.
*/
/datum/antagonist/proc/remove_blacklisted_quirks()
var/mob/living/L = owner.current
if(istype(L))
@@ -209,16 +291,22 @@ GLOBAL_LIST_EMPTY(antagonists)
to_chat(L, "<span class='boldannounce'>[initial(Q.antag_removal_text)]</span>")
qdel(Q)
//Returns the team antagonist belongs to if any.
/**
* Proc that will return the team this antagonist belongs to, when called. Helpful with antagonists that may belong to multiple potential teams in a single round, like families.
*/
/datum/antagonist/proc/get_team()
return
//Individual roundend report
/**
* Proc that sends string information for the end-round report window to the server.
* This runs on every instance of every antagonist that exists at the end of the round.
* This is the body of the message, sandwiched between roundend_report_header and roundend_report_footer.
*/
/datum/antagonist/proc/roundend_report()
var/list/report = list()
if(!owner)
CRASH("antagonist datum without owner")
CRASH("Antagonist datum without owner")
report += printplayer(owner)
@@ -237,11 +325,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return report.Join("<br>")
//Displayed at the start of roundend_category section, default to roundend_category header
/**
* Proc that sends string data for the round-end report.
* Displayed before roundend_report and roundend_report_footer.
* Appears at start of roundend_catagory section.
*/
/datum/antagonist/proc/roundend_report_header()
return "<span class='header'>The [roundend_category] were:</span><br>"
return "<span class='header'>The [roundend_category] were:</span><br>"
//Displayed at the end of roundend_category section
/**
* Proc that sends string data for the round-end report.
* Displayed after roundend_report and roundend_report_footer.
* Appears at the end of the roundend_catagory section.
*/
/datum/antagonist/proc/roundend_report_footer()
return
@@ -250,22 +346,24 @@ GLOBAL_LIST_EMPTY(antagonists)
//Called when using admin tools to give antag status
/datum/antagonist/proc/admin_add(datum/mind/new_owner,mob/admin)
message_admins("[key_name_admin(admin)] made [new_owner.current] into [name].")
log_admin("[key_name(admin)] made [new_owner.current] into [name].")
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into [name].")
log_admin("[key_name(admin)] made [key_name(new_owner)] into [name].")
new_owner.add_antag_datum(src)
//Called when removing antagonist using admin tools
/datum/antagonist/proc/admin_remove(mob/user)
if(!user)
return
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [owner.current].")
log_admin("[key_name(user)] has removed [name] antagonist status from [owner.current].")
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [key_name_admin(owner)].")
log_admin("[key_name(user)] has removed [name] antagonist status from [key_name(owner)].")
on_removal()
//gamemode/proc/is_mode_antag(antagonist/A) => TRUE/FALSE
//Additional data to display in antagonist panel section
//nuke disk code, genome count, etc
/**
* Additional data to display in the antagonist panel section.
* For example, nuke disk code, genome count, etc
*/
/datum/antagonist/proc/antag_panel_data()
return ""
@@ -277,7 +375,7 @@ GLOBAL_LIST_EMPTY(antagonists)
return FALSE
return TRUE
// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/// List of ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/datum/antagonist/proc/get_admin_commands()
. = list()
@@ -344,14 +442,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
/**
* Gets how fast we can hijack the shuttle, return 0 for can not hijack.
* Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
*/
/datum/antagonist/proc/hijack_speed()
var/datum/objective/hijack/H = locate() in objectives
if(!isnull(H?.hijack_speed_override))
return H.hijack_speed_override
return hijack_speed
/// Gets our threat level. Defaults to threat var, override for custom stuff like different traitor goals having different threats.
/**
* Gets our threat level. Override this proc for custom functionality/dynamic threat level.
*/
/datum/antagonist/proc/threat()
. = CONFIG_GET(keyed_list/antag_threat)[lowertext(name)]
if(. == null)
@@ -361,6 +464,13 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/custom
antagpanel_category = "Custom"
show_name_in_check_antagonists = TRUE //They're all different
var/datum/team/custom_team
/datum/antagonist/custom/create_team(datum/team/team)
custom_team = team
/datum/antagonist/custom/get_team()
return custom_team
/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
@@ -389,7 +499,7 @@ GLOBAL_LIST_EMPTY(antagonists)
for(var/datum/objective/objective in objectives)
objective_data += list(list(
"count" = objective_count,
"name" = objective.objective_name,
"name" = objective.name,
"explanation" = objective.explanation_text,
"complete" = objective.completed,
))
@@ -23,7 +23,7 @@
return
var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS)
if((!lungs && !HAS_TRAIT(owner, TRAIT_NOBREATH)) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
if((!lungs && (HAS_TRAIT_FROM(owner, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) || !HAS_TRAIT(owner, TRAIT_NOBREATH))) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
replace_lungs(lungs)
return
@@ -0,0 +1,40 @@
/datum/team/ashwalkers
name = "Ashwalkers"
show_roundend_report = FALSE
var/list/players_spawned = new
/datum/antagonist/ashwalker
name = "\improper Ash Walker"
job_rank = ROLE_LAVALAND
show_in_antagpanel = FALSE
show_to_ghosts = TRUE
antagpanel_category = "Ash Walkers"
var/datum/team/ashwalkers/ashie_team
/datum/antagonist/ashwalker/create_team(datum/team/team)
if(team)
ashie_team = team
objectives |= ashie_team.objectives
else
ashie_team = new
/datum/antagonist/ashwalker/get_team()
return ashie_team
/datum/antagonist/ashwalker/on_body_transfer(mob/living/old_body, mob/living/new_body)
. = ..()
RegisterSignal(new_body, COMSIG_MOB_EXAMINATE, .proc/on_examinate)
/datum/antagonist/ashwalker/on_gain()
. = ..()
RegisterSignal(owner.current, COMSIG_MOB_EXAMINATE, .proc/on_examinate)
/datum/antagonist/ashwalker/on_removal()
. = ..()
UnregisterSignal(owner.current, COMSIG_MOB_EXAMINATE)
/datum/antagonist/ashwalker/proc/on_examinate(datum/source, atom/A)
SIGNAL_HANDLER
if(istype(A, /obj/structure/headpike))
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "headspear", /datum/mood_event/sacrifice_good)
@@ -45,7 +45,7 @@
GLOB.poi_list -= src
return ..()
/obj/structure/blob/core/ex_act(severity, target)
/obj/structure/blob/core/ex_act(severity, target, origin)
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
take_damage(damage, BRUTE, "bomb", 0)
@@ -17,11 +17,13 @@
B.objectives += objective
M.add_antag_datum(B)
var/begin_message = "<span class='deadsay'><b>[L]</b> has been brainwashed with the following objectives: "
var/begin_message = " has been brainwashed with the following objectives: "
var/obj_message = english_list(directives)
var/end_message = "</b>.</span>"
var/end_message = "."
var/rendered = begin_message + obj_message + end_message
deadchat_broadcast(rendered, follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_REGULAR)
deadchat_broadcast(rendered, "<b>[L]</b>", follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_ANNOUNCEMENT)
if(prob(1) || SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
L.say("You son of a bitch! I'm in.", forced = "That son of a bitch! They're in.")
/datum/antagonist/brainwashed
name = "Brainwashed Victim"
@@ -30,19 +32,21 @@
show_in_antagpanel = TRUE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
ui_name = "AntagInfoBrainwashed"
suicide_cry = "FOR... SOMEONE!!"
/datum/antagonist/brainwashed/greet()
to_chat(owner, "<span class='warning'>Your mind reels as it begins focusing on a single purpose...</span>")
to_chat(owner, "<big><span class='warning'><b>Follow the Directives, at any cost!</b></span></big>")
var/i = 1
for(var/X in objectives)
var/datum/objective/O = X
to_chat(owner, "<b>[i].</b> [O.explanation_text]")
i++
/datum/antagonist/brainwashed/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["objectives"] = get_objectives()
return data
/datum/antagonist/brainwashed/farewell()
to_chat(owner, "<span class='warning'>Your mind suddenly clears...</span>")
to_chat(owner, "<big><span class='warning'><b>You feel the weight of the Directives disappear! You no longer have to obey them.</b></span></big>")
to_chat(owner, span_warning("Your mind suddenly clears..."))
to_chat(owner, "<big>[span_warning("<b>You feel the weight of the Directives disappear! You no longer have to obey them.</b>")]</big>")
if(owner.current)
var/mob/living/owner_mob = owner.current
owner_mob.log_message("is no longer brainwashed with the objectives: [english_list(objectives)].", LOG_ATTACK)
owner.announce_objectives()
/datum/antagonist/brainwashed/admin_add(datum/mind/new_owner,mob/admin)
@@ -54,9 +58,9 @@
var/objective = stripped_input(admin, "Add an objective, or leave empty to finish.", "Brainwashing", null, MAX_MESSAGE_LEN)
if(objective)
objectives += objective
while(alert(admin,"Add another objective?","More Brainwashing","Yes","No") == "Yes")
while(tgui_alert(admin,"Add another objective?","More Brainwashing",list("Yes","No")) == "Yes")
if(alert(admin,"Confirm Brainwashing?","Are you sure?","Yes","No") == "No")
if(tgui_alert(admin,"Confirm Brainwashing?","Are you sure?",list("Yes","No")) == "No")
return
if(!LAZYLEN(objectives))
@@ -69,6 +73,7 @@
brainwash(C, objectives)
var/obj_list = english_list(objectives)
message_admins("[key_name_admin(admin)] has brainwashed [key_name_admin(C)] with the following objectives: [obj_list].")
C.log_message("has been force-brainwashed with the objective '[obj_list]' by admin [key_name(admin)]", LOG_ATTACK, log_globally = FALSE)
log_admin("[key_name(admin)] has brainwashed [key_name(C)] with the following objectives: [obj_list].")
/datum/objective/brainwashing
@@ -69,7 +69,7 @@
do_sparks(5, TRUE, AM)
if(isliving(AM))
var/mob/living/L = AM
L.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
L.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash/static)
L.clear_fullscreen("flash", 5)
var/obj/item/transfer_valve/TTV = locate() in L.GetAllContents()
if(TTV)
@@ -8,7 +8,7 @@
linked.examine(user)
return ..()
/obj/effect/clockwork/overlay/ex_act()
/obj/effect/clockwork/overlay/ex_act(severity, target, origin)
return FALSE
/obj/effect/clockwork/overlay/singularity_act()
@@ -36,7 +36,7 @@
return TRUE
. = ..()
/obj/effect/clockwork/sigil/ex_act(severity)
/obj/effect/clockwork/sigil/ex_act(severity, target, origin)
visible_message("<span class='warning'>[src] scatters into thousands of particles.</span>")
qdel(src)
@@ -204,7 +204,7 @@
. = ..()
update_icon()
/obj/effect/clockwork/sigil/transmission/ex_act(severity)
/obj/effect/clockwork/sigil/transmission/ex_act(severity, target, origin)
if(severity == 3)
adjust_clockwork_power(500) //Light explosions charge the network!
visible_message("<span class='warning'>[src] flares a brilliant orange!</span>")
@@ -38,7 +38,7 @@
/obj/effect/clockwork/servant_blocker/singularity_pull()
return
/obj/effect/clockwork/servant_blocker/ex_act(severity, target)
/obj/effect/clockwork/servant_blocker/ex_act(severity, target, origin)
return
/obj/effect/clockwork/servant_blocker/safe_throw_at()
@@ -102,7 +102,7 @@
return TRUE
return ..()
/obj/effect/clockwork/spatial_gateway/ex_act(severity)
/obj/effect/clockwork/spatial_gateway/ex_act(severity, target, origin)
if(severity == 1 && uses)
uses = 0
visible_message("<span class='warning'>[src] is disrupted!</span>")
@@ -251,7 +251,7 @@
name = "stable gateway"
is_stable = TRUE
/obj/effect/clockwork/spatial_gateway/stable/ex_act(severity)
/obj/effect/clockwork/spatial_gateway/stable/ex_act(severity, target, origin)
if(severity == 1)
start_shutdown() //Yes, you can chain devastation-level explosions to delay a gateway shutdown, if you somehow manage to do it without breaking the obelisk. Is it worth it? Probably not.
return TRUE
@@ -53,7 +53,7 @@
user.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30)
addtimer(CALLBACK(user, /mob/living.proc/dropItemToGround, src, TRUE), 1) //equipped happens before putting stuff on(but not before picking items up), 1). thus, we need to wait for it to be on before forcing it off.
/obj/item/clothing/head/helmet/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
/obj/item/clothing/head/helmet/clockwork/mob_can_equip(M, equipper, slot, disable_warning, bypass_equip_delay_self)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
@@ -98,7 +98,7 @@
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
/obj/item/clothing/suit/armor/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
/obj/item/clothing/suit/armor/clockwork/mob_can_equip(M, equipper, slot, disable_warning, bypass_equip_delay_self)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
@@ -158,7 +158,7 @@
max_heat_protection_temperature = initial(max_heat_protection_temperature)
min_cold_protection_temperature = initial(min_cold_protection_temperature)
/obj/item/clothing/gloves/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
/obj/item/clothing/gloves/clockwork/mob_can_equip(M, equipper, slot, disable_warning, bypass_equip_delay_self)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
@@ -208,7 +208,7 @@
else
clothing_flags &= ~NOSLIP
/obj/item/clothing/shoes/clockwork/mob_can_equip(mob/M, mob/equipper, slot, disable_warning = 0)
/obj/item/clothing/shoes/clockwork/mob_can_equip(M, equipper, slot, disable_warning, bypass_equip_delay_self)
if(equipper && !is_servant_of_ratvar(equipper))
return 0
return ..()
@@ -264,7 +264,9 @@
ui.open()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
. = list()
. = ..()
if(!.)
return
.["recollection"] = recollecting
.["power"] = DisplayPower(get_clockwork_power())
.["power_unformatted"] = get_clockwork_power()
@@ -275,6 +277,7 @@
if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff
continue
// FUTURE IMPL: cache these perhaps?
var/list/data = list()
data["name"] = S.name
data["descname"] = S.descname
@@ -214,5 +214,5 @@
sleep(3) //so the animation completes properly
qdel(src)
/obj/effect/clockwork/judicial_marker/ex_act(severity)
/obj/effect/clockwork/judicial_marker/ex_act(severity, target, origin)
return
@@ -231,3 +231,31 @@
to_chat(invoker, "<span class='nezbere'>\"Only one of my weapons may exist in this temporal stream!\"</span>")
return FALSE
return ..()
/datum/clockwork_scripture/create_object/construct/cogscarab
descname = "Building Construct"
name = "Cogscarab"
desc = "Creates a shell for a cogscarab, a drone that helps build your base!"
invocations = list("Arise, drone!", "Create defenses for the true light!")
channel_time = 80
power_cost = 8000
creator_message = "<span class='brass'>Your slab disgorges several chunks of replicant alloy that form into a spiderlike shell.</span>"
usage_tip = "These machines will help you get a base built up while you go out to look for more followers."
tier = SCRIPTURE_APPLICATION
one_per_tile = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Creates a cogscarab, good for the backline."
object_path = /obj/item/clockwork/construct_chassis/cogscarab/
construct_type = /mob/living/simple_animal/drone/cogscarab
combat_construct = FALSE
/datum/clockwork_scripture/create_object/construct/cogscarab/update_construct_limit()
var/human_servants = 0
for(var/V in SSticker.mode.servants_of_ratvar)
var/datum/mind/M = V
var/mob/living/L = M.current
if(ishuman(L) && L.stat != DEAD)
human_servants++
construct_limit = round(clamp((human_servants / 4), 1, 3)) //1 per 4 human servants, maximum of 3
@@ -141,7 +141,7 @@
if(isliving(M.current) && M.current.stat != DEAD)
var/turf/t_turf = isAI(M.current) ? get_step(get_step(src, NORTH),NORTH) : get_turf(src) // AI too fat, must make sure it always ends up a 2 tiles north instead of on the ark.
do_teleport(M.current, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE)
M.current.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
M.current.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
M.current.clear_fullscreen("flash", 5)
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, FALSE)
recalls_remaining--
@@ -197,7 +197,7 @@
glow = new /obj/effect/clockwork/overlay/gateway_glow(get_turf(src))
glow.linked = src
/obj/structure/destructible/clockwork/massive/celestial_gateway/ex_act(severity)
/obj/structure/destructible/clockwork/massive/celestial_gateway/ex_act(severity, target, origin)
var/damage = max((obj_integrity * 0.7) / severity, 100) //requires multiple bombs to take down
take_damage(damage, BRUTE, "bomb", 0)
@@ -311,7 +311,7 @@
var/turf/T = get_turf(M)
if(is_servant_of_ratvar(M) && (!T || T.z != z))
M.forceMove(get_step(src, SOUTH))
M.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
M.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
M.clear_fullscreen("flash", 5)
progress_in_seconds += GATEWAY_SUMMON_RATE
switch(progress_in_seconds)
@@ -6,6 +6,7 @@
job_rank = ROLE_SERVANT_OF_RATVAR
antag_moodlet = /datum/mood_event/cult
skill_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/level/dwarfy/blacksmithing)
ui_name = "AntagInfoClockwork"
var/datum/action/innate/hierophant/hierophant_network = new
threat = 3
var/datum/team/clockcult/clock_team
@@ -14,6 +15,12 @@
var/ignore_eligibility_check = FALSE
var/ignore_holy_water = FALSE
/datum/antagonist/clockcult/ui_data(mob/user)
. = ..()
if(!.)
return
.["HONOR_RATVAR"] = GLOB.ratvar_awakens
/datum/antagonist/clockcult/silent
name = "Silent Clock Cultist"
silent = TRUE
@@ -22,6 +29,8 @@
/datum/antagonist/clockcult/neutered
name = "Neutered Clock Cultist"
neutered = TRUE
soft_antag = TRUE
ui_name = null // no.
/datum/antagonist/clockcult/neutered/traitor
name = "Traitor Clock Cultist"
@@ -57,14 +66,6 @@
if(. && !ignore_eligibility_check)
. = is_eligible_servant(new_owner.current)
/datum/antagonist/clockcult/greet()
if(!owner.current || silent)
return
owner.current.visible_message("<span class='heavy_brass'>[owner.current]'s eyes glow a blazing yellow!</span>", null, null, 7, owner.current) //don't show the owner this message
to_chat(owner.current, "<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork \
Justiciar above all else. Perform his every whim without hesitation.</span>")
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 70, FALSE, pressure_affected = FALSE)
/datum/antagonist/clockcult/on_gain()
var/mob/living/current = owner.current
SSticker.mode.servants_of_ratvar += owner
@@ -240,3 +241,18 @@
parts += printplayerlist(members - eminence)
return "<div class='panel clockborder'>[parts.Join("<br>")]</div>"
//I have no idea where to put this so I'm leaving it here. Loads reebe. Only one reebe can exist, so it's checked via a global var.
/proc/load_reebe()
if(GLOB.reebe_loaded)
return TRUE
var/list/errorList = list()
var/list/reebes = SSmapping.LoadGroup(errorList, "Reebe", "map_files/generic", "City_of_Cogs.dmm", default_traits = ZTRAITS_REEBE, silent = TRUE)
if(errorList.len) // reebe failed to load
message_admins("Reebe failed to load!")
log_game("Reebe failed to load!")
return FALSE
for(var/datum/parsed_map/PM in reebes)
PM.initTemplateBounds()
GLOB.reebe_loaded = TRUE
return TRUE
+105 -77
View File
@@ -387,8 +387,7 @@
log_combat(user, M, "used a cult spell on", source.name, "")
M.set_last_attacker(user)
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
. = ..()
/obj/item/melee/blood_magic/proc/post_cast(atom/target, mob/living/carbon/user, proximity)
if(invocation)
user.whisper(invocation, language = /datum/language/common)
if(health_cost)
@@ -467,6 +466,7 @@
to_chat(user, "<span class='cultitalic'>In an brilliant flash of red, [L] [is_servant_of_ratvar(L) ? "writhes in pain!" : "falls to the ground!"]</span>")
uses--
..()
post_cast(target, user, proximity)
//Teleportation
/obj/item/melee/blood_magic/teleport
@@ -480,40 +480,45 @@
to_chat(user, "<span class='warning'>You can only teleport adjacent cultists with this spell!</span>")
return
if(iscultist(user))
var/list/potential_runes = list()
var/list/teleportnames = list()
for(var/R in GLOB.teleport_runes)
var/obj/effect/rune/teleport/T = R
potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T
if(!potential_runes.len)
to_chat(user, "<span class='warning'>There are no valid runes to teleport to!</span>")
log_game("Teleport talisman failed - no other teleport runes")
return
var/turf/T = get_turf(src)
if(is_away_level(T.z))
to_chat(user, "<span class='cultitalic'>You are not in the right dimension!</span>")
log_game("Teleport spell failed - user in away mission")
return
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
if(QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated() || !actual_selected_rune || !proximity)
return
var/turf/dest = get_turf(actual_selected_rune)
if(is_blocked_turf(dest, TRUE))
to_chat(user, "<span class='warning'>The target rune is blocked. Attempting to teleport to it would be massively unwise.</span>")
return
uses--
var/turf/origin = get_turf(user)
var/mob/living/L = target
if(do_teleport(L, dest, channel = TELEPORT_CHANNEL_CULT))
origin.visible_message("<span class='warning'>Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] with a sharp crack!</span>", \
"<span class='cultitalic'>You speak the words of the talisman and find yourself somewhere else!</span>", "<i>You hear a sharp crack.</i>")
dest.visible_message("<span class='warning'>There is a boom of outrushing air as something appears above the rune!</span>", null, "<i>You hear a boom.</i>")
try_teleport(target, user, proximity)
..()
/obj/item/melee/blood_magic/teleport/proc/try_teleport(atom/target, mob/living/carbon/user, proximity)
set waitfor = FALSE
var/list/potential_runes = list()
var/list/teleportnames = list()
for(var/R in GLOB.teleport_runes)
var/obj/effect/rune/teleport/T = R
potential_runes[avoid_assoc_duplicate_keys(T.listkey, teleportnames)] = T
if(!potential_runes.len)
to_chat(user, "<span class='warning'>There are no valid runes to teleport to!</span>")
log_game("Teleport talisman failed - no other teleport runes")
return
var/turf/T = get_turf(src)
if(is_away_level(T.z))
to_chat(user, "<span class='cultitalic'>You are not in the right dimension!</span>")
log_game("Teleport spell failed - user in away mission")
return
var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked
var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to?
if(QDELETED(src) || !user || !user.is_holding(src) || user.incapacitated() || !actual_selected_rune || !proximity)
return
var/turf/dest = get_turf(actual_selected_rune)
if(is_blocked_turf(dest, TRUE))
to_chat(user, "<span class='warning'>The target rune is blocked. Attempting to teleport to it would be massively unwise.</span>")
return
uses--
var/turf/origin = get_turf(user)
var/mob/living/L = target
if(do_teleport(L, dest, channel = TELEPORT_CHANNEL_CULT))
origin.visible_message("<span class='warning'>Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] with a sharp crack!</span>", \
"<span class='cultitalic'>You speak the words of the talisman and find yourself somewhere else!</span>", "<i>You hear a sharp crack.</i>")
dest.visible_message("<span class='warning'>There is a boom of outrushing air as something appears above the rune!</span>", null, "<i>You hear a boom.</i>")
post_cast(target, user, proximity)
//Shackles
/obj/item/melee/blood_magic/shackles
name = "Binding Aura"
@@ -530,6 +535,7 @@
user.visible_message("<span class='cultitalic'>This victim doesn't have enough arms to complete the restraint!</span>")
return
..()
post_cast(target, user, proximity)
/obj/item/melee/blood_magic/shackles/proc/CuffAttack(mob/living/carbon/C, mob/living/user)
if(!C.handcuffed)
@@ -583,6 +589,7 @@
to_chat(user, "<span class='warning'>A dark cloud emanates from your hand and swirls around the metal, twisting it into a construct shell!</span>")
new /obj/structure/constructshell(T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
post_cast(target, proximity_flag, user)
else
to_chat(user, "<span class='warning'>You need 50 metal to produce a construct shell!</span>")
else if(istype(target, /obj/item/stack/sheet/plasteel))
@@ -593,56 +600,73 @@
new /obj/item/stack/sheet/runed_metal(T,quantity)
to_chat(user, "<span class='warning'>A dark cloud emanates from you hand and swirls around the plasteel, transforming it into runed metal!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
post_cast(target, user, proximity_flag)
if(istype(target, /obj/item/clothing/suit/hooded/wintercoat) && target.type != /obj/item/clothing/suit/hooded/wintercoat/narsie)
if (do_after(user,30,target=target))
new /obj/item/clothing/suit/hooded/wintercoat/narsie(T)
qdel(target)
to_chat(user, "<span class='warning'>A dark cloud emanates from you hand and swirls around [target], transforming it into a narsian winter coat!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
convert_coat(target, user, proximity_flag, T)
else if(istype(target,/mob/living/silicon/robot))
var/mob/living/silicon/robot/candidate = target
if(!iscultist(user, TRUE))
to_chat(user, "<span class='warning'>You are not strongly connected enough to Nar'sie to use make constructs...</span>")
else if(candidate.mmi)
user.visible_message("<span class='danger'>A dark cloud emanates from [user]'s hand and swirls around [candidate]!</span>")
playsound(T, 'sound/machines/airlock_alien_prying.ogg', 80, 1)
var/prev_color = candidate.color
candidate.color = "black"
if(do_after(user, 90, target = candidate))
candidate.emp_act(80)
var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
user.visible_message("<span class='danger'>The dark cloud receedes from what was formerly [candidate], revealing a\n [construct_class]!</span>")
switch(construct_class)
if("Juggernaut")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, candidate, user, 0, T)
if("Wraith")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, candidate, user, 0, T)
if("Artificer")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, candidate, user, 0, T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
uses--
candidate.mmi = null
qdel(candidate)
else
candidate.color = prev_color
else
uses--
to_chat(user, "<span class='warning'>A dark cloud emanates from you hand and swirls around [candidate] - twisting it into a construct shell!</span>")
new /obj/structure/constructshell(T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
convert_borg(target, user, proximity_flag, T)
else if(istype(target,/obj/machinery/door/airlock))
playsound(T, 'sound/machines/airlockforced.ogg', 50, 1)
do_sparks(5, TRUE, target)
if(do_after(user, 50, target = user))
target.narsie_act()
uses--
user.visible_message("<span class='warning'>Black ribbons suddenly emanate from [user]'s hand and cling to the airlock - twisting and corrupting it!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
convert_door(target, user, proximity_flag, T)
else
to_chat(user, "<span class='warning'>The spell will not work on [target]!</span>")
return
..()
/obj/item/melee/blood_magic/construction/proc/convert_coat(atom/target, mob/user, proximity_flag, turf/T)
set waitfor = FALSE
if(do_after(user,30,target=target))
new /obj/item/clothing/suit/hooded/wintercoat/narsie(T)
qdel(target)
to_chat(user, "<span class='warning'>A dark cloud emanates from you hand and swirls around [target], transforming it into a narsian winter coat!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
post_cast(target, user, proximity_flag)
/obj/item/melee/blood_magic/construction/proc/convert_door(atom/target, mob/user, proximity_flag, turf/T)
set waitfor = FALSE
playsound(T, 'sound/machines/airlockforced.ogg', 50, 1)
do_sparks(5, TRUE, target)
if(do_after(user, 50, target = user))
target.narsie_act()
uses--
user.visible_message("<span class='warning'>Black ribbons suddenly emanate from [user]'s hand and cling to the airlock - twisting and corrupting it!</span>")
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
post_cast(target, user, proximity_flag)
/obj/item/melee/blood_magic/construction/proc/convert_borg(atom/target, mob/user, proximity_flag, turf/T)
set waitfor = FALSE
var/mob/living/silicon/robot/candidate = target
if(!iscultist(user, TRUE))
to_chat(user, "<span class='warning'>You are not strongly connected enough to Nar'sie to use make constructs...</span>")
return
else if(candidate.mmi)
user.visible_message("<span class='danger'>A dark cloud emanates from [user]'s hand and swirls around [candidate]!</span>")
playsound(T, 'sound/machines/airlock_alien_prying.ogg', 80, 1)
var/prev_color = candidate.color
candidate.color = "black"
if(do_after(user, 90, target = candidate))
candidate.emp_act(80)
var/construct_class = alert(user, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer")
user.visible_message("<span class='danger'>The dark cloud receedes from what was formerly [candidate], revealing a\n [construct_class]!</span>")
switch(construct_class)
if("Juggernaut")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, candidate, user, 0, T)
if("Wraith")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, candidate, user, 0, T)
if("Artificer")
makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, candidate, user, 0, T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
uses--
candidate.mmi = null
qdel(candidate)
else
candidate.color = prev_color
else
uses--
to_chat(user, "<span class='warning'>A dark cloud emanates from you hand and swirls around [candidate] - twisting it into a construct shell!</span>")
new /obj/structure/constructshell(T)
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
post_cast(target, user, proximity_flag)
//Armor: Gives the target a basic cultist combat loadout
/obj/item/melee/blood_magic/armor
name = "Bladed Aura"
@@ -665,6 +689,7 @@
C.put_in_hands(new /obj/item/melee/cultblade(user))
C.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user))
..()
post_cast(target, user, proximity)
/obj/item/melee/blood_magic/manipulator
name = "Ritual Aura"
@@ -693,7 +718,9 @@
H.adjust_integration_blood(uses * 2)
to_chat(user,"<span class='danger'>You use the last of your blood rites to restore what blood you could!</span>")
uses = 0
return ..()
..()
post_cast(target, user, proximity)
return
else
H.blood_volume = (BLOOD_VOLUME_SAFE*H.blood_ratio)
uses -= round(restore_blood/2)
@@ -758,6 +785,7 @@
if(istype(target, /obj/effect/decal/cleanable/blood))
blood_draw(target, user)
..()
post_cast()
/obj/item/melee/blood_magic/manipulator/proc/blood_draw(atom/target, mob/living/carbon/human/user)
var/temp = 0
+31 -29
View File
@@ -660,40 +660,42 @@
return
if(istype(A, /obj/item))
var/list/cultists = list()
for(var/datum/mind/M in SSticker.mode.cult)
if(M.current && M.current.stat != DEAD)
cultists |= M.current
var/mob/living/cultist_to_receive = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in (cultists - user)
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
return
if(!cultist_to_receive)
to_chat(user, "<span class='cult italic'>You require a destination!</span>")
log_game("Void torch failed - no target")
return
if(cultist_to_receive.stat == DEAD)
to_chat(user, "<span class='cult italic'>[cultist_to_receive] has died!</span>")
log_game("Void torch failed - target died")
return
if(!iscultist(cultist_to_receive))
to_chat(user, "<span class='cult italic'>[cultist_to_receive] is not a follower of the Geometer!</span>")
log_game("Void torch failed - target was deconverted")
return
if(A in user.GetAllContents())
to_chat(user, "<span class='cult italic'>[A] must be on a surface in order to teleport it!</span>")
return
to_chat(user, "<span class='cult italic'>You ignite [A] with \the [src], turning it to ash, but through the torch's flames you see that [A] has reached [cultist_to_receive]!")
cultist_to_receive.put_in_hands(A)
charges--
to_chat(user, "\The [src] now has [charges] charge\s.")
if(charges == 0)
qdel(src)
transmit_item(A, user, proximity)
else
..()
to_chat(user, "<span class='warning'>\The [src] can only transport items!</span>")
/obj/item/flashlight/flare/culttorch/proc/transmit_item(atom/movable/A, mob/user, proximity)
set waitfor = FALSE
var/list/cultists = list()
for(var/datum/mind/M in SSticker.mode.cult)
if(M.current && M.current.stat != DEAD)
cultists |= M.current
var/mob/living/cultist_to_receive = input(user, "Who do you wish to call to [src]?", "Followers of the Geometer") as null|anything in (cultists - user)
if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated())
return
if(!cultist_to_receive)
to_chat(user, "<span class='cult italic'>You require a destination!</span>")
log_game("Void torch failed - no target")
return
if(cultist_to_receive.stat == DEAD)
to_chat(user, "<span class='cult italic'>[cultist_to_receive] has died!</span>")
log_game("Void torch failed - target died")
return
if(!iscultist(cultist_to_receive))
to_chat(user, "<span class='cult italic'>[cultist_to_receive] is not a follower of the Geometer!</span>")
log_game("Void torch failed - target was deconverted")
return
if(A in user.GetAllContents())
to_chat(user, "<span class='cult italic'>[A] must be on a surface in order to teleport it!</span>")
return
to_chat(user, "<span class='cult italic'>You ignite [A] with \the [src], turning it to ash, but through the torch's flames you see that [A] has reached [cultist_to_receive]!")
cultist_to_receive.put_in_hands(A)
charges--
to_chat(user, "\The [src] now has [charges] charge\s.")
if(charges == 0)
qdel(src)
/obj/item/cult_spear
name = "blood halberd"
@@ -104,7 +104,7 @@
/mob/living/carbon/true_devil/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
return 666
/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, override_protection = 0)
/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/tiled/flash, override_protection = 0)
if(mind && has_bane(BANE_LIGHT))
mind.disrupt_spells(-500)
return ..() //flashes don't stop devils UNLESS it's their bane.
@@ -179,7 +179,7 @@
/mob/living/carbon/true_devil/is_literate()
return 1
/mob/living/carbon/true_devil/ex_act(severity, ex_target)
/mob/living/carbon/true_devil/ex_act(severity, target, origin)
if(!ascended)
var/b_loss
switch (severity)
@@ -3,5 +3,6 @@
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
threat = 2
ui_name = "AntagInfoMorph"
//It does nothing! (Besides tracking)
@@ -4,3 +4,5 @@
show_name_in_check_antagonists = TRUE
threat = 5
show_to_ghosts = TRUE
ui_name = "AntagInfoNightmare"
suicide_cry = "FOR THE DARKNESS!!"
@@ -236,6 +236,8 @@
var/obj/machinery/nuclearbomb/tracked_nuke
var/core_objective = /datum/objective/nuclear
var/memorized_code
var/list/team_discounts
var/datum/weakref/war_button_ref
/datum/team/nuclear/New()
..()
@@ -121,7 +121,7 @@
return
var/mob/living/carbon/human/H = owner.current
// Give uplink
var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src)
var/obj/item/uplink_holder = owner.equip_traitor()
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
uplink.telecrystals = INITIAL_CRYSTALS
// Give AI hacking board
@@ -174,7 +174,7 @@
//Immunities
/mob/living/simple_animal/revenant/ex_act(severity, target)
/mob/living/simple_animal/revenant/ex_act(severity, target, origin)
return 1 //Immune to the effects of explosions.
/mob/living/simple_animal/revenant/wave_ex_act(power, datum/wave_explosion/explosion, dir)
@@ -249,7 +249,7 @@
released and fully healed, because in the end it's just a jape, \
sibling!</B>"
/mob/living/simple_animal/slaughter/laughter/ex_act(severity)
/mob/living/simple_animal/slaughter/laughter/ex_act(severity, target, origin)
switch(severity)
if(1)
death()
@@ -7,37 +7,51 @@
/datum/antagonist/traitor/internal_affairs
name = "Internal Affairs Agent"
employer = "Nanotrasen"
special_role = "internal affairs agent"
suicide_cry = "FOR THE COMPANY!!"
antagpanel_category = "IAA"
var/special_role = "internal affairs agent"
var/syndicate = FALSE
var/last_man_standing = FALSE
var/list/datum/mind/targets_stolen
/datum/antagonist/traitor/internal_affairs/New()
. = ..()
LAZYADD(targets_stolen, src)
/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
if(owner && owner.current)
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
.=..() //in case the base is used in future
if(owner && owner.current)
. = ..()
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
give_pinpointer(owner.current)
/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
.=..()
if(owner && owner.current)
. = ..()
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/on_gain()
START_PROCESSING(SSprocessing, src)
.=..()
. = ..()
/datum/antagonist/traitor/internal_affairs/on_removal()
STOP_PROCESSING(SSprocessing,src)
.=..()
. = ..()
/datum/antagonist/traitor/internal_affairs/process()
iaa_process()
/datum/status_effect/agent_pinpointer
id = "agent_pinpointer"
duration = -1
@@ -46,6 +60,8 @@
var/minimum_range = PINPOINTER_MINIMUM_RANGE
var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
var/mob/scan_target = null
var/range_mid = 8
var/range_far = 16
/atom/movable/screen/alert/status_effect/agent_pinpointer
name = "Internal Affairs Integrated Pinpointer"
@@ -66,13 +82,13 @@
linked_alert.icon_state = "pinondirect"
else
linked_alert.setDir(get_dir(here, there))
switch(get_dist(here, there))
if(1 to 8)
linked_alert.icon_state = "pinonclose"
if(9 to 16)
linked_alert.icon_state = "pinonmedium"
if(16 to INFINITY)
linked_alert.icon_state = "pinonfar"
var/dist = (get_dist(here, there))
if(dist >= 1 && dist <= range_mid)
linked_alert.icon_state = "pinonclose"
else if(dist > range_mid && dist <= range_far)
linked_alert.icon_state = "pinonmedium"
else if(dist > range_far)
linked_alert.icon_state = "pinonfar"
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
scan_target = null
@@ -99,37 +115,42 @@
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
/datum/antagonist/traitor/proc/replace_escape_objective()
if(!owner || !objectives.len)
if(!owner)
CRASH("Antag datum with no owner.")
if(!objectives.len)
return
for (var/objective_ in objectives)
if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)))
for (var/objective in objectives)
if(!(istype(objective, /datum/objective/escape) || istype(objective, /datum/objective/survive)))
continue
remove_objective(objective_)
objectives -= objective
var/datum/objective/martyr/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
/datum/antagonist/traitor/proc/reinstate_escape_objective()
if(!owner||!objectives.len)
if(!owner)
CRASH("Antag datum with no owner.")
if(!objectives.len)
return
for (var/objective_ in objectives)
if(!istype(objective_, /datum/objective/martyr))
for (var/objective in objectives)
if(!istype(objective, /datum/objective/martyr))
continue
remove_objective(objective_)
remove_objective(objective)
/datum/antagonist/traitor/internal_affairs/reinstate_escape_objective()
..()
var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
for (var/datum/objective/martyr/martyr_objective in objectives)
objectives -= martyr_objective
var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
add_objective(escape_objective)
objectives += escape_objective
/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
if(!owner.current||owner.current.stat==DEAD)
return
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
LAZYINITLIST(targets_stolen)
to_chat(owner.current, span_userdanger("Target eliminated: [victim.name]"))
for(var/objective_ in victim.get_all_objectives())
if(istype(objective_, /datum/objective/assassinate/internal))
var/datum/objective/assassinate/internal/objective = objective_
@@ -143,7 +164,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
else if(istype(objective_, /datum/objective/destroy/internal))
var/datum/objective/destroy/internal/objective = objective_
var/datum/objective/destroy/internal/new_objective = new
@@ -156,7 +177,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
last_man_standing = TRUE
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
@@ -167,13 +188,15 @@
return
if(last_man_standing)
if(syndicate)
to_chat(owner.current,"<span class='userdanger'>All the suspected agents are dead, and no more is required of you. Die a glorious death, agent.</span>")
replace_escape_objective(owner)
to_chat(owner.current,span_userdanger("All the suspected agents are dead, and no more is required of you. Die a glorious death, agent."))
else
to_chat(owner.current,"<span class='userdanger'>All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base.</span>")
to_chat(owner.current,span_userdanger("All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base."))
replace_escape_objective(owner)
/datum/antagonist/traitor/internal_affairs/proc/iaa_process()
if(owner&&owner.current&&owner.current.stat!=DEAD)
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current && owner.current.stat != DEAD)
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
continue
@@ -188,12 +211,12 @@
objective.stolen = TRUE
else
if(objective.stolen)
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
var/fail_msg = span_userdanger("Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! ")
if(last_man_standing)
if(syndicate)
fail_msg += "<span class='userdanger'> You no longer have permission to die. </span>"
fail_msg += span_userdanger(" You no longer have permission to die. ")
else
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to great shame.</span>"
fail_msg += span_userdanger(" The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.")
reinstate_escape_objective(owner)
last_man_standing = FALSE
to_chat(owner.current, fail_msg)
@@ -220,29 +243,24 @@
/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
forge_iaa_objectives()
var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
add_objective(escape_objective)
/datum/antagonist/traitor/internal_affairs/proc/greet_iaa()
var/crime = pick("distribution of contraband" , "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
var/crime = pick("distribution of contraband", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
to_chat(owner.current, "<span class='userdanger'>You are the [special_role].</span>")
to_chat(owner.current, span_userdanger("You are the [special_role]."))
if(syndicate)
to_chat(owner.current, "<span class='userdanger'>GREAT LEADER IS DEAD. NANOTRASEN MUST FALL.</span>")
to_chat(owner.current, "<span class='userdanger'>Your have infiltrated this vessel to cause chaos and assassinate targets known to have conspired against the Syndicate.</span>")
to_chat(owner.current, "<span class='userdanger'>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</span>")
to_chat(owner.current, "<span class='userdanger'>You have been provided with a standard uplink to accomplish your task. </span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you are a Syndicate agent. By no means reveal that your targets are being hunted.</span>")
to_chat(owner.current, span_userdanger("Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court."))
to_chat(owner.current, "<span class='warningplain'><B><font size=5 color=red>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</font></B></span>")
to_chat(owner.current, span_userdanger("You have been provided with a standard uplink to accomplish your task."))
else
to_chat(owner.current, "<span class='userdanger'>CAUTION: Your legal status as a citizen of NanoTrasen will be permanently revoked upon completion of your first contract.</span>")
to_chat(owner.current, "<span class='userdanger'>Your target has been suspected of [crime], and must be removed from this plane.</span>")
to_chat(owner.current, "<span class='userdanger'>While you have a license to kill, you are to eliminate your targets with no collateral or unrelated deaths.</span>")
to_chat(owner.current, "<span class='userdanger'>For the sake of plausable deniability, you have been equipped with captured Syndicate equipment via uplink.</span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you, or any other NT employees, are undercover agents.</span>")
to_chat(owner.current, span_userdanger("Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial."))
to_chat(owner.current, "<span class='warningplain'><B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B></span>")
to_chat(owner.current, span_userdanger("For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink."))
to_chat(owner.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
to_chat(owner.current, span_userdanger("Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them."))
owner.announce_objectives()
/datum/antagonist/traitor/internal_affairs/greet()
@@ -5,7 +5,7 @@
chaos = 5
threat = 5
min_players = 20
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_TC/contract_kit)
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_tc/contract_kit)
/datum/traitor_class/human/martyr/forge_objectives(datum/antagonist/traitor/T)
var/datum/objective/martyr/O = new
@@ -11,6 +11,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/// Minimum players for this to randomly roll via get_random_traitor_kind().
var/min_players = 0
var/list/uplink_filters
/// Specific tgui theme for the player's antag info panel.
var/tgui_theme = "syndicate"
/datum/traitor_class/New()
..()
@@ -5,14 +5,22 @@
job_rank = ROLE_TRAITOR
antag_moodlet = /datum/mood_event/focused
skill_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
var/special_role = ROLE_TRAITOR
hijack_speed = 0.5 //10 seconds per hijack stage by default
ui_name = "AntagInfoTraitor"
suicide_cry = "FOR THE SYNDICATE!!"
var/employer = "The Syndicate"
var/give_objectives = TRUE
var/should_give_codewords = TRUE
var/should_equip = TRUE
///special datum about what kind of employer the trator has
var/datum/traitor_class/traitor_kind
///reference to the uplink this traitor was given, if they were.
var/datum/component/uplink/uplink
var/datum/contractor_hub/contractor_hub
hijack_speed = 0.5 //10 seconds per hijack stage by default
threat = 5
/datum/antagonist/traitor/New()
@@ -45,7 +53,7 @@
/datum/antagonist/traitor/process()
traitor_kind.on_process(src)
/proc/get_random_traitor_kind(var/list/blacklist = list())
/proc/get_random_traitor_kind(list/blacklist = list())
var/list/weights = list()
for(var/C in GLOB.traitor_classes)
if(!(C in blacklist))
@@ -62,23 +70,24 @@
return choice
/datum/antagonist/traitor/on_gain()
owner.special_role = job_rank
if(owner.current && isAI(owner.current))
set_traitor_kind(TRAITOR_AI)
else
set_traitor_kind(get_random_traitor_kind())
SSticker.mode.traitors += owner
owner.special_role = special_role
finalize_traitor()
..()
uplink = owner.find_syndicate_uplink()
return ..()
/datum/antagonist/traitor/on_removal()
if(!silent && owner.current)
to_chat(owner.current,span_userdanger("You are no longer the [job_rank]!"))
//Remove malf powers.
traitor_kind.on_removal(src)
SSticker.mode.traitors -= owner
if(!silent && owner.current)
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
owner.special_role = null
. = ..()
return ..()
/datum/antagonist/traitor/proc/handle_hearing(datum/source, list/hearing_args)
var/message = hearing_args[HEARING_RAW_MESSAGE]
@@ -93,6 +102,7 @@
/datum/antagonist/traitor/proc/remove_objective(datum/objective/O)
objectives -= O
/// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack.
/datum/antagonist/traitor/proc/forge_traitor_objectives()
traitor_kind.forge_objectives(src)
@@ -151,21 +161,42 @@
H.dna.add_mutation(CLOWNMUT)
UnregisterSignal(M, COMSIG_MOVABLE_HEAR)
/datum/antagonist/traitor/ui_static_data(mob/user)
var/list/data = list()
data["phrases"] = jointext(GLOB.syndicate_code_phrase, ", ")
data["responses"] = jointext(GLOB.syndicate_code_response, ", ")
data["theme"] = traitor_kind.tgui_theme //traitor_flavor["ui_theme"]
data["code"] = uplink.unlock_code
data["intro"] = "You are from [traitor_kind.employer]." //traitor_flavor["introduction"]
data["allies"] = "Most other syndicate operatives are not to be trusted (but try not to rat them out), as they might have been assigned opposing objectives." //traitor_flavor["allies"]
data["goal"] = "We do not approve of mindless killing of innocent workers; \"get in, get done, get out\" is our motto." //traitor_flavor["goal"]
data["has_uplink"] = uplink ? TRUE : FALSE
if(uplink)
data["uplink_intro"] = "You have been provided with a standard uplink to accomplish your task." //traitor_flavor["uplink"]
data["uplink_unlock_info"] = uplink.unlock_text
data["objectives"] = get_objectives()
return data
/// Outputs this shift's codewords and responses to the antag's chat and copies them to their memory.
/datum/antagonist/traitor/proc/give_codewords()
if(!owner.current)
return
var/mob/traitor_mob=owner.current
var/mob/traitor_mob = owner.current
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var/dat = "<U><B>The Syndicate have provided you with the following codewords to identify fellow agents:</B></U>\n"
dat += "<B>Code Phrase</B>: <span class='blue'>[phrases]</span>\n"
dat += "<B>Code Response</B>: <span class='red'>[responses]</span>"
to_chat(traitor_mob, dat)
to_chat(traitor_mob, "<U><B>The Syndicate have provided you with the following codewords to identify fellow agents:</B></U>")
to_chat(traitor_mob, "<B>Code Phrase</B>: [span_blue("[phrases]")]")
to_chat(traitor_mob, "<B>Code Response</B>: [span_red("[responses]")]")
antag_memory += "<b>Code Phrase</b>: <span class='blue'>[phrases]</span><br>"
antag_memory += "<b>Code Response</b>: <span class='red'>[responses]</span><br>"
antag_memory += "<b>Code Phrase</b>: [span_blue("[phrases]")]<br>"
antag_memory += "<b>Code Response</b>: [span_red("[responses]")]<br>"
to_chat(traitor_mob, "Use the codewords during regular conversation to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
to_chat(traitor_mob, span_alertwarning("You memorize the codewords, allowing you to recognise them when heard."))
/datum/antagonist/traitor/proc/add_law_zero()
var/mob/living/silicon/ai/killer = owner.current
@@ -220,44 +251,43 @@
where = "In your [equipped_slot]"
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
//TODO Collate
/datum/antagonist/traitor/roundend_report()
var/list/result = list()
var/traitorwin = TRUE
var/traitor_won = TRUE
result += printplayer(owner)
var/TC_uses = 0
var/uplink_true = FALSE
var/used_telecrystals = 0
var/uplink_owned = FALSE
var/purchases = ""
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
if(H)
TC_uses = H.total_spent
uplink_true = TRUE
purchases += H.generate_render(FALSE)
// Uplinks add an entry to uplink_purchase_logs_by_key on init.
var/datum/uplink_purchase_log/purchase_log = GLOB.uplink_purchase_logs_by_key[owner.key]
if(purchase_log)
used_telecrystals = purchase_log.total_spent
uplink_owned = TRUE
purchases += purchase_log.generate_render(FALSE)
var/objectives_text = ""
if(objectives.len)//If the traitor had no objectives, don't need to process this.
if(objectives.len) //If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_greentext("Success!")]"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
traitor_won = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text]"
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
count++
if(uplink_true)
var/uplink_text = "(used [TC_uses] TC) [purchases]"
if(TC_uses==0 && traitorwin)
if(uplink_owned)
var/uplink_text = "(used [used_telecrystals] TC) [purchases]"
if((used_telecrystals == 0) && traitor_won)
var/static/icon/badass = icon('icons/badass.dmi', "badass")
uplink_text += "<BIG>[icon2html(badass, world)]</BIG>"
result += uplink_text
@@ -266,55 +296,59 @@
var/special_role_text = lowertext(name)
if(contractor_hub)
if (contractor_hub)
result += contractor_round_end()
if(traitorwin)
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
if(traitor_won)
result += span_greentext("The [special_role_text] was successful!")
else
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
result += span_redtext("The [special_role_text] has failed!")
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
return result.Join("<br>")
/// Proc detailing contract kit buys/completed contracts/additional info
/datum/antagonist/traitor/proc/contractor_round_end()
var result = ""
var total_spent_rep = 0
var/result = ""
var/total_spent_rep = 0
var/completed_contracts = 0
var/completed_contracts = contractor_hub.contracts_completed
var/tc_total = contractor_hub.contract_TC_payed_out + contractor_hub.contract_TC_to_redeem
for(var/datum/syndicate_contract/contract in contractor_hub.assigned_contracts)
if(contract.status == CONTRACT_STATUS_COMPLETE)
completed_contracts++
var/contractor_item_icons = "" // Icons of purchases
var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
for(var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items) // Get all the icons/total cost for all our items bought
/// Get all the icons/total cost for all our items bought
for (var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items)
contractor_item_icons += "<span class='tooltip_container'>\[ <i class=\"fas [contractor_purchase.item_icon]\"></i><span class='tooltip_hover'><b>[contractor_purchase.name] - [contractor_purchase.cost] Rep</b><br><br>[contractor_purchase.desc]</span> \]</span>"
total_spent_rep += contractor_purchase.cost
if(istype(contractor_purchase, /datum/contractor_item/contractor_partner)) // Special case for reinforcements, we want to show their ckey and name on round end.
/// Special case for reinforcements, we want to show their ckey and name on round end.
if (istype(contractor_purchase, /datum/contractor_item/contractor_partner))
var/datum/contractor_item/contractor_partner/partner = contractor_purchase
contractor_support_unit += "<br><b>[partner.partner_mind.key]</b> played <b>[partner.partner_mind.current.name]</b>, their contractor support unit."
if (contractor_hub.purchased_items.len)
result += "<br>(used [total_spent_rep] Rep)"
result += "<br>(used [total_spent_rep] Rep) "
result += contractor_item_icons
result += "<br>"
if(completed_contracts > 0)
if (completed_contracts > 0)
var/pluralCheck = "contract"
if(completed_contracts > 1)
if (completed_contracts > 1)
pluralCheck = "contracts"
result += "Completed <span class='greentext'>[completed_contracts]</span> [pluralCheck] for a total of \
<span class='greentext'>[tc_total] TC</span>!<br>"
result += "Completed [span_greentext("[completed_contracts]")] [pluralCheck] for a total of \
[span_greentext("[tc_total] TC")]![contractor_support_unit]<br>"
return result
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
<b>The code responses were:</b> <span class='redtext'>[responses]</span><br>"
var/message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
<b>The code responses were:</b> [span_redtext("[responses]")]<br>"
return message
+34 -42
View File
@@ -3,12 +3,15 @@
roundend_category = "wizards/witches"
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
antag_hud_type = ANTAG_HUD_WIZ
antag_hud_name = "wizard"
antag_moodlet = /datum/mood_event/focused
threat = 30
hijack_speed = 0.5
ui_name = "AntagInfoWizard"
suicide_cry = "FOR THE FEDERATION!!"
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
var/hud_version = "wizard"
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
@@ -50,16 +53,17 @@
wiz_team = new(owner)
wiz_team.name = "[owner.current.real_name] team"
wiz_team.master_wizard = src
update_wiz_icons_added(owner.current)
add_antag_hud(antag_hud_type, antag_hud_name, owner.current)
/datum/antagonist/wizard/proc/send_to_lair()
if(!owner || !owner.current)
if(!owner)
CRASH("Antag datum with no owner.")
if(!owner.current)
return
if(!GLOB.wizardstart.len)
SSjob.SendToLateJoin(owner.current)
to_chat(owner, "HOT INSERTION, GO GO GO")
else
owner.current.forceMove(pick(GLOB.wizardstart))
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/flavor/wizard/new_objective = new
@@ -79,7 +83,7 @@
/datum/antagonist/wizard/proc/equip_wizard()
if(!owner)
return
CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -91,18 +95,14 @@
H.age = wiz_age
H.equipOutfit(outfit_type)
/datum/antagonist/wizard/greet()
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
owner.announce_objectives()
to_chat(owner, "<B>These are merely guidelines! The federation are your masters, but you forge your own path!</B>")
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
to_chat(owner,"<B>Remember:</B> do not forget to prepare your spells.")
/datum/antagonist/wizard/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["objectives"] = get_objectives()
return data
/datum/antagonist/wizard/farewell()
to_chat(owner, "<span class='userdanger'>You have been brainwashed! You are no longer a wizard!</span>")
to_chat(owner, span_userdanger("You have been brainwashed! You are no longer a wizard!"))
/datum/antagonist/wizard/proc/rename_wizard()
set waitfor = FALSE
@@ -111,7 +111,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
var/newname = reject_bad_name(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN))
var/newname = sanitize_name(reject_bad_text(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN)))
if (!newname)
newname = randomname
@@ -120,12 +120,12 @@
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
add_antag_hud(antag_hud_type, antag_hud_name, M)
M.faction |= ROLE_WIZARD
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_removed(M)
remove_antag_hud(antag_hud_type, M)
M.faction -= ROLE_WIZARD
@@ -138,7 +138,7 @@
/datum/antagonist/wizard/apprentice
name = "Wizard Apprentice"
hud_version = "apprentice"
antag_hud_name = "apprentice"
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
@@ -157,7 +157,7 @@
/datum/antagonist/wizard/apprentice/equip_wizard()
. = ..()
if(!owner)
return
CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -169,12 +169,12 @@
if(APPRENTICE_BLUESPACE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality-bending mobility spells. You are able to cast teleport and ethereal jaunt.")
if(APPRENTICE_HEALING)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned life-saving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null))
@@ -194,6 +194,7 @@
//Random event wizard
/datum/antagonist/wizard/apprentice/imposter
name = "Wizard Imposter"
show_in_antagpanel = FALSE
allow_rename = FALSE
move_to_lair = FALSE
@@ -224,20 +225,11 @@
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz,join = TRUE)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.join_hud(wiz)
set_antag_hud(wiz, hud_version)
/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.leave_hud(wiz)
set_antag_hud(wiz, null)
/datum/antagonist/wizard/academy
name = "Academy Teacher"
show_in_antagpanel = FALSE
outfit_type = /datum/outfit/wizard/academy
move_to_lair = FALSE
/datum/antagonist/wizard/academy/equip_wizard()
. = ..()
@@ -250,7 +242,7 @@
if(!istype(M))
return
var/obj/item/implant/exile/Implant = new
var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(M)
Implant.implant(M)
/datum/antagonist/wizard/academy/create_objectives()
@@ -265,25 +257,25 @@
parts += printplayer(owner)
var/count = 1
var/wizardwin = 1
var/wizardwin = TRUE
for(var/datum/objective/objective in objectives)
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_greentext("Success!")]"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
wizardwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(wizardwin)
parts += "<span class='greentext'>The wizard was successful!</span>"
parts += span_greentext("The wizard was successful!")
else
parts += "<span class='redtext'>The wizard has failed!</span>"
parts += span_redtext("The wizard has failed!")
if(owner.spell_list.len>0)
parts += "<B>[owner.name] used the following spells: </B>"
+2 -1
View File
@@ -74,7 +74,7 @@
if(spill && R.total_volume >= 5)
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
if(!turfing)
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1), log = TRUE)
G.last_orgasmed = world.time
R.clear_reagents()
@@ -117,6 +117,7 @@
if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
return
to_chat(src,"<span class='userlove'>You used your [G.name] to fill [container].</span>")
message_admins("[src] used their [G.name] to fill [container].")
do_climax(fluid_source, container, G, FALSE)
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
+12 -8
View File
@@ -34,14 +34,16 @@
/obj/item/assembly/proc/on_attach()
/obj/item/assembly/proc/on_detach() //call this when detaching it from a device. handles any special functions that need to be updated ex post facto
///Call this when detaching it from a device. handles any special functions that need to be updated ex post facto
/obj/item/assembly/proc/on_detach()
if(!holder)
return FALSE
forceMove(holder.drop_location())
holder = null
return TRUE
/obj/item/assembly/proc/holder_movement() //Called when the holder is moved
///Called when the holder is moved
/obj/item/assembly/proc/holder_movement()
if(!holder)
return FALSE
setDir(holder.dir)
@@ -53,7 +55,6 @@
return FALSE
return TRUE
//Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
/obj/item/assembly/proc/pulsed(radio = FALSE)
if(wire_type & WIRE_RECEIVE)
@@ -62,7 +63,6 @@
INVOKE_ASYNC(src, .proc/activate)
return TRUE
//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
/obj/item/assembly/proc/pulse(radio = FALSE)
if(connected && wire_type)
@@ -74,7 +74,6 @@
holder.process_activation(src, 0, 1)
return TRUE
// What the device does when turned on
/obj/item/assembly/proc/activate()
if(QDELETED(src) || !secured || (next_activate > world.time))
@@ -82,13 +81,11 @@
next_activate = world.time + activate_cooldown
return TRUE
/obj/item/assembly/proc/toggle_secure()
secured = !secured
update_icon()
return secured
/obj/item/assembly/attackby(obj/item/W, mob/user, params)
if(isassembly(W))
var/obj/item/assembly/A = W
@@ -115,7 +112,6 @@
. = ..()
. += "<span class='notice'>\The [src] [secured? "is secured and ready to be used!" : "can be attached to other things."]</span>"
/obj/item/assembly/attack_self(mob/user)
if(!user)
return FALSE
@@ -125,3 +121,11 @@
/obj/item/assembly/interact(mob/user)
return ui_interact(user)
/obj/item/assembly/ui_host(mob/user)
if(holder)
return holder
return src
/obj/item/assembly/ui_state(mob/user)
return GLOB.hands_state
+3 -3
View File
@@ -88,14 +88,14 @@
if(a_right)
a_right.dropped(user)
/obj/item/assembly_holder/on_attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
/obj/item/assembly_holder/on_attack_hand(user, act_intent)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess
. = ..()
if(.)
return
if(a_left)
a_left.attack_hand()
a_left.attack_hand(user, act_intent)
if(a_right)
a_right.attack_hand()
a_right.attack_hand(user, act_intent)
/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool)
if(..())
-28
View File
@@ -13,9 +13,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
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
register()
@@ -368,28 +365,3 @@ 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]!")
+163 -120
View File
@@ -1,11 +1,5 @@
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/tgui_common
keep_local_name = TRUE
assets = list(
"tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
)
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
@@ -54,9 +48,14 @@
/datum/asset/simple/radar_assets
assets = list(
"ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
"ntosradarbackground.png" = 'icons/ui_icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/ui_icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/ui_icons/tgui/ntosradar_pointer_S.png'
)
/datum/asset/simple/circuit_assets
assets = list(
"grid_background.png" = 'icons/ui_icons/tgui/grid_background.png'
)
/datum/asset/spritesheet/simple/pda
@@ -91,6 +90,7 @@
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
// "droneblacklist" = 'icons/pda_icons/pda_droneblacklist.png',
)
/datum/asset/spritesheet/simple/paper
@@ -116,7 +116,7 @@
/datum/asset/simple/irv
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js',
)
/datum/asset/group/irv
@@ -125,35 +125,10 @@
/datum/asset/simple/irv
)
/datum/asset/simple/namespaced/changelog
assets = list(
"88x31.png" = 'html/88x31.png',
"bug-minus.png" = 'html/bug-minus.png',
"cross-circle.png" = 'html/cross-circle.png',
"hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png',
"image-minus.png" = 'html/image-minus.png',
"image-plus.png" = 'html/image-plus.png',
"music-minus.png" = 'html/music-minus.png',
"music-plus.png" = 'html/music-plus.png',
"tick-circle.png" = 'html/tick-circle.png',
"wrench-screwdriver.png" = 'html/wrench-screwdriver.png',
"spell-check.png" = 'html/spell-check.png',
"burn-exclamation.png" = 'html/burn-exclamation.png',
"chevron.png" = 'html/chevron.png',
"chevron-expand.png" = 'html/chevron-expand.png',
"scales.png" = 'html/scales.png',
"coding.png" = 'html/coding.png',
"ban.png" = 'html/ban.png',
"chrome-wrench.png" = 'html/chrome-wrench.png',
"changelog.css" = 'html/changelog.css'
)
parents = list("changelog.html" = 'html/changelog.html')
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'html/jquery.min.js',
"jquery.min.js" = 'html/jquery/jquery.min.js',
)
/datum/asset/simple/namespaced/fontawesome
@@ -223,102 +198,102 @@
/datum/asset/simple/arcade
assets = list(
"boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
"boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
"boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
"boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
"boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
"boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
"boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
"boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
"boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
"boss4.gif" = 'icons/ui_icons/arcade/boss4.gif',
"boss5.gif" = 'icons/ui_icons/arcade/boss5.gif',
"boss6.gif" = 'icons/ui_icons/arcade/boss6.gif',
)
/datum/asset/spritesheet/simple/achievements
name ="achievements"
assets = list(
"default" = 'icons/UI_Icons/Achievements/default.png',
"basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
"baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
"baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
"bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
"colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
"hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
"drake" = 'icons/UI_Icons/Achievements/Boss/drake.png',
"legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
"miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
"swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
"tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
"featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
"helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
"jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
"meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
"timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
"upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
"clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
"clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
"longshift" = 'icons/UI_Icons/Achievements/Misc/longshift.png',
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
"ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
"fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
"rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
"voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
"toolbox_soul" = 'icons/UI_Icons/Achievements/Misc/toolbox_soul.png',
"chem_tut" = 'icons/UI_Icons/Achievements/Misc/chem_tut.png',
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
"mafia" = 'icons/UI_Icons/Achievements/Mafia/mafia.png',
"town" = 'icons/UI_Icons/Achievements/Mafia/town.png',
"neutral" = 'icons/UI_Icons/Achievements/Mafia/neutral.png',
"hated" = 'icons/UI_Icons/Achievements/Mafia/hated.png',
"basemafia" ='icons/UI_Icons/Achievements/basemafia.png',
"frenching" = 'icons/UI_Icons/Achievements/Misc/frenchingthebubble.png'
"default" = 'icons/ui_icons/achievements/default.png',
"basemisc" = 'icons/ui_icons/achievements/basemisc.png',
"baseboss" = 'icons/ui_icons/achievements/baseboss.png',
"baseskill" = 'icons/ui_icons/achievements/baseskill.png',
"bbgum" = 'icons/ui_icons/achievements/Boss/bbgum.png',
"colossus" = 'icons/ui_icons/achievements/Boss/colossus.png',
"hierophant" = 'icons/ui_icons/achievements/Boss/hierophant.png',
"drake" = 'icons/ui_icons/achievements/Boss/drake.png',
"legion" = 'icons/ui_icons/achievements/Boss/legion.png',
"miner" = 'icons/ui_icons/achievements/Boss/miner.png',
"swarmer" = 'icons/ui_icons/achievements/Boss/swarmer.png',
"tendril" = 'icons/ui_icons/achievements/Boss/tendril.png',
"featofstrength" = 'icons/ui_icons/achievements/Misc/featofstrength.png',
"helbital" = 'icons/ui_icons/achievements/Misc/helbital.png',
"jackpot" = 'icons/ui_icons/achievements/Misc/jackpot.png',
"meteors" = 'icons/ui_icons/achievements/Misc/meteors.png',
"timewaste" = 'icons/ui_icons/achievements/Misc/timewaste.png',
"upgrade" = 'icons/ui_icons/achievements/Misc/upgrade.png',
"clownking" = 'icons/ui_icons/achievements/Misc/clownking.png',
"clownthanks" = 'icons/ui_icons/achievements/Misc/clownthanks.png',
"rule8" = 'icons/ui_icons/achievements/Misc/rule8.png',
"longshift" = 'icons/ui_icons/achievements/Misc/longshift.png',
"snail" = 'icons/ui_icons/achievements/Misc/snail.png',
"ascension" = 'icons/ui_icons/achievements/Misc/ascension.png',
"ashascend" = 'icons/ui_icons/achievements/Misc/ashascend.png',
"fleshascend" = 'icons/ui_icons/achievements/Misc/fleshascend.png',
"rustascend" = 'icons/ui_icons/achievements/Misc/rustascend.png',
"voidascend" = 'icons/ui_icons/achievements/Misc/voidascend.png',
"toolbox_soul" = 'icons/ui_icons/achievements/Misc/toolbox_soul.png',
"chem_tut" = 'icons/ui_icons/achievements/Misc/chem_tut.png',
"mining" = 'icons/ui_icons/achievements/Skills/mining.png',
"mafia" = 'icons/ui_icons/achievements/Mafia/mafia.png',
"town" = 'icons/ui_icons/achievements/Mafia/town.png',
"neutral" = 'icons/ui_icons/achievements/Mafia/neutral.png',
"hated" = 'icons/ui_icons/achievements/Mafia/hated.png',
"basemafia" ='icons/ui_icons/achievements/basemafia.png',
"frenching" = 'icons/ui_icons/achievements/Misc/frenchingthebubble.png'
)
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
"pill1" = 'icons/UI_Icons/Pills/pill1.png',
"pill2" = 'icons/UI_Icons/Pills/pill2.png',
"pill3" = 'icons/UI_Icons/Pills/pill3.png',
"pill4" = 'icons/UI_Icons/Pills/pill4.png',
"pill5" = 'icons/UI_Icons/Pills/pill5.png',
"pill6" = 'icons/UI_Icons/Pills/pill6.png',
"pill7" = 'icons/UI_Icons/Pills/pill7.png',
"pill8" = 'icons/UI_Icons/Pills/pill8.png',
"pill9" = 'icons/UI_Icons/Pills/pill9.png',
"pill10" = 'icons/UI_Icons/Pills/pill10.png',
"pill11" = 'icons/UI_Icons/Pills/pill11.png',
"pill12" = 'icons/UI_Icons/Pills/pill12.png',
"pill13" = 'icons/UI_Icons/Pills/pill13.png',
"pill14" = 'icons/UI_Icons/Pills/pill14.png',
"pill15" = 'icons/UI_Icons/Pills/pill15.png',
"pill16" = 'icons/UI_Icons/Pills/pill16.png',
"pill17" = 'icons/UI_Icons/Pills/pill17.png',
"pill18" = 'icons/UI_Icons/Pills/pill18.png',
"pill19" = 'icons/UI_Icons/Pills/pill19.png',
"pill20" = 'icons/UI_Icons/Pills/pill20.png',
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
"pill1" = 'icons/ui_icons/pills/pill1.png',
"pill2" = 'icons/ui_icons/pills/pill2.png',
"pill3" = 'icons/ui_icons/pills/pill3.png',
"pill4" = 'icons/ui_icons/pills/pill4.png',
"pill5" = 'icons/ui_icons/pills/pill5.png',
"pill6" = 'icons/ui_icons/pills/pill6.png',
"pill7" = 'icons/ui_icons/pills/pill7.png',
"pill8" = 'icons/ui_icons/pills/pill8.png',
"pill9" = 'icons/ui_icons/pills/pill9.png',
"pill10" = 'icons/ui_icons/pills/pill10.png',
"pill11" = 'icons/ui_icons/pills/pill11.png',
"pill12" = 'icons/ui_icons/pills/pill12.png',
"pill13" = 'icons/ui_icons/pills/pill13.png',
"pill14" = 'icons/ui_icons/pills/pill14.png',
"pill15" = 'icons/ui_icons/pills/pill15.png',
"pill16" = 'icons/ui_icons/pills/pill16.png',
"pill17" = 'icons/ui_icons/pills/pill17.png',
"pill18" = 'icons/ui_icons/pills/pill18.png',
"pill19" = 'icons/ui_icons/pills/pill19.png',
"pill20" = 'icons/ui_icons/pills/pill20.png',
"pill21" = 'icons/ui_icons/pills/pill21.png',
"pill22" = 'icons/ui_icons/pills/pill22.png',
)
// /datum/asset/spritesheet/simple/condiments
// name = "condiments"
// assets = list(
// CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
// "enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
// "flour" = 'icons/UI_Icons/Condiments/flour.png',
// "mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
// "milk" = 'icons/UI_Icons/Condiments/milk.png',
// "blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
// "rice" = 'icons/UI_Icons/Condiments/rice.png',
// "sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
// "soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
// "soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
// "sugar" = 'icons/UI_Icons/Condiments/sugar.png',
// "ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
// "capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
// "frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
// "bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
// "cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
// CONDIMASTER_STYLE_FALLBACK = 'icons/ui_icons/condiments/emptycondiment.png',
// "enzyme" = 'icons/ui_icons/condiments/enzyme.png',
// "flour" = 'icons/ui_icons/condiments/flour.png',
// "mayonnaise" = 'icons/ui_icons/condiments/mayonnaise.png',
// "milk" = 'icons/ui_icons/condiments/milk.png',
// "blackpepper" = 'icons/ui_icons/condiments/peppermillsmall.png',
// "rice" = 'icons/ui_icons/condiments/rice.png',
// "sodiumchloride" = 'icons/ui_icons/condiments/saltshakersmall.png',
// "soymilk" = 'icons/ui_icons/condiments/soymilk.png',
// "soysauce" = 'icons/ui_icons/condiments/soysauce.png',
// "sugar" = 'icons/ui_icons/condiments/sugar.png',
// "ketchup" = 'icons/ui_icons/condiments/ketchup.png',
// "capsaicin" = 'icons/ui_icons/condiments/hotsauce.png',
// "frostoil" = 'icons/ui_icons/condiments/coldsauce.png',
// "bbqsauce" = 'icons/ui_icons/condiments/bbqsauce.png',
// "cornoil" = 'icons/ui_icons/condiments/oliveoil.png',
// )
//this exists purely to avoid meta by pre-loading all language icons.
@@ -410,9 +385,15 @@
if (machine)
item = machine
// Check for GAGS support where necessary
// var/greyscale_config = initial(item.greyscale_config)
// var/greyscale_colors = initial(item.greyscale_colors)
// if (greyscale_config && greyscale_colors)
// icon_file = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
// else
icon_file = initial(item.icon)
icon_state = initial(item.icon_state)
icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
@@ -441,7 +422,11 @@
if (!ispath(item, /atom))
continue
var/icon_file = initial(item.icon)
var/icon_file
// if (initial(item.greyscale_colors) && initial(item.greyscale_config))
// icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
// else
icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
@@ -543,6 +528,35 @@
// Insert(id, fish_icon, fish_icon_state)
// ..()
/datum/asset/simple/adventure
assets = list(
"default" = 'icons/ui_icons/adventure/default.png',
"grue" = 'icons/ui_icons/adventure/grue.png',
"signal_lost" ='icons/ui_icons/adventure/signal_lost.png',
"trade" = 'icons/ui_icons/adventure/trade.png',
)
/datum/asset/simple/inventory
assets = list(
"inventory-glasses.png" = 'icons/ui_icons/inventory/glasses.png',
"inventory-head.png" = 'icons/ui_icons/inventory/head.png',
"inventory-neck.png" = 'icons/ui_icons/inventory/neck.png',
"inventory-mask.png" = 'icons/ui_icons/inventory/mask.png',
"inventory-ears.png" = 'icons/ui_icons/inventory/ears.png',
"inventory-uniform.png" = 'icons/ui_icons/inventory/uniform.png',
"inventory-suit.png" = 'icons/ui_icons/inventory/suit.png',
"inventory-gloves.png" = 'icons/ui_icons/inventory/gloves.png',
"inventory-hand_l.png" = 'icons/ui_icons/inventory/hand_l.png',
"inventory-hand_r.png" = 'icons/ui_icons/inventory/hand_r.png',
"inventory-shoes.png" = 'icons/ui_icons/inventory/shoes.png',
"inventory-suit_storage.png" = 'icons/ui_icons/inventory/suit_storage.png',
"inventory-id.png" = 'icons/ui_icons/inventory/id.png',
"inventory-belt.png" = 'icons/ui_icons/inventory/belt.png',
"inventory-back.png" = 'icons/ui_icons/inventory/back.png',
"inventory-pocket.png" = 'icons/ui_icons/inventory/pocket.png',
"inventory-collar.png" = 'icons/ui_icons/inventory/collar.png',
)
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
@@ -550,5 +564,34 @@
/datum/asset/simple/tutorial_advisors
assets = list(
"chem_help_advisor.gif" = 'icons/UI_Icons/Advisors/chem_help_advisor.gif',
"chem_help_advisor.gif" = 'icons/ui_icons/advisors/chem_help_advisor.gif',
)
// /datum/asset/spritesheet/moods
// name = "moods"
// var/iconinserted = 1
// /datum/asset/spritesheet/moods/register()
// for(var/i in 1 to 9)
// var/target_to_insert = "mood"+"[iconinserted]"
// Insert(target_to_insert, 'icons/hud/screen_gen.dmi', target_to_insert)
// iconinserted++
// ..()
// /datum/asset/spritesheet/moods/ModifyInserted(icon/pre_asset)
// var/blended_color
// switch(iconinserted)
// if(1)
// blended_color = "#f15d36"
// if(2 to 3)
// blended_color = "#f38943"
// if(4)
// blended_color = "#dfa65b"
// if(5)
// blended_color = "#4b96c4"
// if(6)
// blended_color = "#86d656"
// else
// blended_color = "#2eeb9a"
// pre_asset.Blend(blended_color, ICON_MULTIPLY)
// return pre_asset
@@ -115,7 +115,7 @@
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
to_chat(client, "Sending Resources...")
to_chat(client, "<span class='infoplain'>Sending Resources...</span>")
for (var/asset_name in unreceived)
var/new_asset_name = asset_name
+52 -11
View File
@@ -43,6 +43,7 @@
)
)
fusion_power = 3
enthalpy = -393500
/datum/gas/plasma
id = GAS_PLASMA
@@ -54,7 +55,10 @@
heat_penalty = 15
transmit_modifier = 4
powermix = 1
// no fire info cause it has its own bespoke reaction for trit generation reasons
fire_burn_rate = OXYGEN_BURN_RATE_BASE // named when plasma fires were the only fires, surely
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
fire_products = "plasma_fire"
enthalpy = FIRE_PLASMA_ENERGY_RELEASED // 3000000, 3 megajoules, 3000 kj
/datum/gas/water_vapor
id = GAS_H2O
@@ -64,6 +68,7 @@
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
heat_penalty = 8
enthalpy = -241800 // FIRE_HYDROGEN_ENERGY_RELEASED is actually what this was supposed to be
powermix = 1
breath_reagent = /datum/reagent/water
@@ -84,6 +89,7 @@
fire_products = list(GAS_N2 = 1)
oxidation_rate = 0.5
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
enthalpy = 81600
heat_resistance = 6
/datum/gas/nitryl
@@ -95,8 +101,22 @@
flags = GAS_FLAG_DANGEROUS
fusion_power = 15
fire_products = list(GAS_N2 = 0.5)
enthalpy = 33200
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/hydrogen
id = GAS_HYDROGEN
specific_heat = 10
name = "Hydrogen"
flags = GAS_FLAG_DANGEROUS
fusion_power = 0
powermix = 1
heat_penalty = 3
transmit_modifier = 10
fire_products = list(GAS_H2O = 1)
fire_burn_rate = 2
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/tritium
id = GAS_TRITIUM
specific_heat = 10
@@ -108,13 +128,11 @@
powermix = 1
heat_penalty = 10
transmit_modifier = 30
/*
these are for when we add hydrogen, trit gets to keep its hardcoded fire for legacy reasons
fire_provides = list(GAS_H2O = 2)
fire_products = list(GAS_H2O = 1)
enthalpy = 40000
fire_burn_rate = 2
fire_energy_released = FIRE_HYDROGEN_ENERGY_RELEASED
fire_radiation_released = 50 // arbitrary number, basically 60 moles of trit burning will just barely start to harm you
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
*/
/datum/gas/bz
id = GAS_BZ
@@ -124,6 +142,7 @@
fusion_power = 8
powermix = 1
heat_penalty = 5
enthalpy = FIRE_CARBON_ENERGY_RELEASED // it is a mystery
transmit_modifier = -2
radioactivity_modifier = 5
@@ -139,7 +158,8 @@
name = "Pluoxium"
fusion_power = 10
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 1000 // it is VERY stable
oxidation_rate = 8
oxidation_rate = 8 // when it can oxidize, it can oxidize a LOT
enthalpy = -50000 // but it reduces the heat output a bit
powermix = -1
heat_penalty = -1
transmit_modifier = -5
@@ -172,7 +192,7 @@
alert_type = /atom/movable/screen/alert/too_much_ch4
)
)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
enthalpy = -74600
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
/datum/gas/methyl_bromide
@@ -192,7 +212,28 @@
alert_type = /atom/movable/screen/alert/too_much_ch3br
)
)
fire_products = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BZ = 0.5)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_burn_rate = 0.5
fire_products = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BROMINE = 0.5)
enthalpy = -35400
fire_burn_rate = 4 / 7 // oh no
fire_temperature = 808 // its autoignition, it apparently doesn't spark readily, so i don't put it lower
/datum/gas/bromine
id = GAS_BROMINE
specific_heat = 76
name = "Bromine"
flags = GAS_FLAG_DANGEROUS
group = GAS_GROUP_CHEMICALS
enthalpy = 193 // yeah it's small but it's good to include it
breath_reagent = /datum/reagent/bromine
/datum/gas/ammonia
id = GAS_AMMONIA
specific_heat = 35
name = "Ammonia"
flags = GAS_FLAG_DANGEROUS
group = GAS_GROUP_CHEMICALS
enthalpy = -45900
breath_reagent = /datum/reagent/ammonia
fire_products = list(GAS_H2O = 1.5, GAS_N2 = 0.5)
fire_burn_rate = 4/3
fire_temperature = 924
+24 -8
View File
@@ -15,6 +15,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
/proc/_auxtools_register_gas(datum/gas/gas) // makes sure auxtools knows stuff about this gas
/datum/auxgm
var/done_initializing = FALSE
var/list/datums = list()
var/list/specific_heats = list()
var/list/names = list()
@@ -32,30 +33,34 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
var/list/oxidation_temperatures = list()
var/list/oxidation_rates = list()
var/list/fire_temperatures = list()
var/list/fire_enthalpies = list()
var/list/enthalpies = list()
var/list/fire_products = list()
var/list/fire_burn_rates = list()
var/list/supermatter = list()
var/list/groups_by_gas = list()
var/list/groups = list()
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
var/gas_overlay = "" //icon_state in icons/effects/atmospherics.dmi
var/color = "#ffff"
var/moles_visible = null
var/flags = NONE //currently used by canisters
var/group = null // groups for scrubber/filter listing
var/fusion_power = 0 // How much the gas destabilizes a fusion reaction
var/breath_results = GAS_CO2 // what breathing this breathes out
var/breath_reagent = null // what breathing this adds to your reagents
var/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/datum/reagent/breath_reagent = null // what breathing this adds to your reagents
var/datum/reagent/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/list/breath_alert_info = null // list for alerts that pop up when you have too much/not enough of something
var/oxidation_temperature = null // temperature above which this gas is an oxidizer; null for none
var/oxidation_rate = 1 // how many moles of this can oxidize how many moles of material
var/fire_temperature = null // temperature above which gas may catch fire; null for none
var/list/fire_products = null // what results when this gas is burned (oxidizer or fuel); null for none
var/fire_energy_released = 0 // how much energy is released per mole of fuel burned
var/enthalpy = 0 // Standard enthalpy of formation in joules, used for fires
var/fire_burn_rate = 1 // how many moles are burned per product released
var/fire_radiation_released = 0 // How much radiation is released when this gas burns
var/powermix = 0 // how much this gas contributes to the supermatter's powermix ratio
var/heat_penalty = 0 // heat and waste penalty from having the supermatter crystal surrounded by this gas; negative numbers reduce
var/transmit_modifier = 0 // bonus to supermatter power generation (multiplicative, since it's % based, and divided by 10)
@@ -94,21 +99,31 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
breath_reagents[g] = gas.breath_reagent
if(gas.breath_reagent_dangerous)
breath_reagents_dangerous[g] = gas.breath_reagent_dangerous
if(gas.oxidation_temperature)
oxidation_temperatures[g] = gas.oxidation_temperature
oxidation_rates[g] = gas.oxidation_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
enthalpies[g] = gas.enthalpy
else if(gas.fire_temperature)
fire_temperatures[g] = gas.fire_temperature
fire_burn_rates[g] = gas.fire_burn_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
enthalpies[g] = gas.enthalpy
if(gas.group)
if(!(gas.group in groups))
groups[gas.group] = list()
groups[gas.group] += gas
groups_by_gas[g] = gas.group
add_supermatter_properties(gas)
_auxtools_register_gas(gas)
if(done_initializing)
for(var/r in SSair.gas_reactions)
var/datum/gas_reaction/R = r
R.init_reqs()
SSair.auxtools_update_reactions()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_GAS, g)
/proc/finalize_gas_refs()
@@ -127,6 +142,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
for(var/breathing_class_path in subtypesof(/datum/breathing_class))
var/datum/breathing_class/class = new breathing_class_path
breathing_classes[breathing_class_path] = class
done_initializing = TRUE
finalize_gas_refs()
@@ -53,23 +53,68 @@
id = "vapor"
/datum/gas_reaction/water_vapor/init_reqs()
min_requirements = list(GAS_H2O = MOLES_GAS_VISIBLE)
min_requirements = list(
GAS_H2O = MOLES_GAS_VISIBLE,
"MAX_TEMP" = T0C + 40
)
/datum/gas_reaction/water_vapor/react(datum/gas_mixture/air, datum/holder)
var/turf/open/location = isturf(holder) ? holder : null
. = NO_REACTION
var/turf/open/location = holder
if(!istype(location))
return NO_REACTION
if (air.return_temperature() <= WATER_VAPOR_FREEZE)
if(location && location.freon_gas_act())
. = REACTING
return REACTING
else if(location && location.water_vapor_gas_act())
air.adjust_moles(GAS_H2O,-MOLES_GAS_VISIBLE)
. = REACTING
return REACTING
// no test cause it's entirely based on location
/datum/gas_reaction/condensation
priority = 0
name = "Condensation"
id = "condense"
exclude = TRUE
var/datum/reagent/condensing_reagent
/datum/gas_reaction/condensation/New(datum/reagent/R)
. = ..()
if(!istype(R))
return
min_requirements = list(
"MAX_TEMP" = initial(R.boiling_point)
)
min_requirements[R.get_gas()] = MOLES_GAS_VISIBLE
name = "[R.name] condensation"
id = "[R.type] condensation"
condensing_reagent = R
exclude = FALSE
/datum/gas_reaction/condensation/react(datum/gas_mixture/air, datum/holder)
. = NO_REACTION
var/turf/open/location = holder
if(!istype(location))
return
var/temperature = air.return_temperature()
var/static/datum/reagents/reagents_holder = new
reagents_holder.clear_reagents()
reagents_holder.chem_temp = temperature
var/G = condensing_reagent.get_gas()
var/amt = air.get_moles(G)
air.adjust_moles(G, -min(initial(condensing_reagent.condensation_amount), amt))
reagents_holder.add_reagent(condensing_reagent, amt)
. = REACTING
for(var/atom/movable/AM in location)
if(location.intact && AM.level == 1)
continue
reagents_holder.reaction(AM, TOUCH)
reagents_holder.reaction(location, TOUCH)
//tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/tritfire
priority = -1 //fire should ALWAYS be last, but tritium fires happen before plasma fires
exclude = TRUE // generic fire now takes care of this
name = "Tritium Combustion"
id = "tritfire"
@@ -88,9 +133,9 @@
item.temperature_expose(air, temperature, CELL_VOLUME)
location.temperature_expose(air, temperature, CELL_VOLUME)
/proc/radiation_burn(turf/open/location, energy_released)
/proc/radiation_burn(turf/open/location, rad_power)
if(istype(location) && prob(10))
radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR)
radiation_pulse(location, rad_power)
/datum/gas_reaction/tritfire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
@@ -151,6 +196,7 @@
/datum/gas_reaction/plasmafire
priority = -2 //fire should ALWAYS be last, but plasma fires happen after tritium fires
name = "Plasma Combustion"
exclude = TRUE // generic fire now takes care of this
id = "plasmafire"
/datum/gas_reaction/plasmafire/init_reqs()
@@ -300,7 +346,7 @@
fuels[fuel] *= oxidation_ratio
fuels += oxidizers
var/list/fire_products = GLOB.gas_data.fire_products
var/list/fire_enthalpies = GLOB.gas_data.fire_enthalpies
var/list/fire_enthalpies = GLOB.gas_data.enthalpies
for(var/fuel in fuels + oxidizers)
var/amt = fuels[fuel]
if(!burn_results[fuel])
@@ -535,7 +535,7 @@
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"set_filters" = list(GAS_CO2, GAS_MIASMA),
"set_filters" = list(GAS_CO2, GAS_MIASMA, GAS_GROUP_CHEMICALS),
"scrubbing" = 1,
"widenet" = 0,
))
@@ -554,6 +554,7 @@
GAS_MIASMA,
GAS_PLASMA,
GAS_H2O,
GAS_HYDROGEN,
GAS_HYPERNOB,
GAS_NITROUS,
GAS_NITRYL,
@@ -562,7 +563,8 @@
GAS_STIMULUM,
GAS_PLUOXIUM,
GAS_METHANE,
GAS_METHYL_BROMIDE
GAS_METHYL_BROMIDE,
GAS_GROUP_CHEMICALS
),
"scrubbing" = 1,
"widenet" = 1,
@@ -582,9 +584,16 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE*2
"set_external_pressure" = ONE_ATMOSPHERE*1.4
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE/1.4
))
if(AALARM_MODE_REFILL)
for(var/device_id in A.air_scrub_names)
@@ -596,10 +605,15 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE * 3
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 0,
))
if(AALARM_MODE_PANIC,
AALARM_MODE_REPLACEMENT)
for(var/device_id in A.air_scrub_names)
@@ -610,8 +624,14 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 0
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 0
))
if(AALARM_MODE_SIPHON)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
@@ -621,9 +641,14 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 0
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 0
))
if(AALARM_MODE_OFF)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
@@ -641,8 +666,12 @@
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 2,
"set_internal_pressure" = 0
"checks" = 0,
"is_pressurizing" = 1
))
send_signal(device_id, list(
"power" = 0,
"is_siphoning" = 1
))
/obj/machinery/airalarm/update_icon_state()
@@ -29,13 +29,16 @@ Passive gate is similar to the regular pump except:
/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user)
if(can_interact(user))
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_icon()
return ..()
/obj/machinery/atmospherics/components/binary/passive_gate/AltClick(mob/user)
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
update_icon()
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output set to [target_pressure] kPa")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/passive_gate/Destroy()
@@ -33,25 +33,19 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its pressure.</span>"
/obj/machinery/atmospherics/components/binary/pump/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
on = !on
update_icon()
investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return ..()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/pump/AltClick(mob/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
to_chat(user,"<span class='notice'>You maximize the pressure on the [src].</span>")
investigate_log("Pump, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Pump, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output set to [target_pressure] kPa")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/pump/Destroy()
SSradio.remove_object(src,frequency)
@@ -33,15 +33,20 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its pressure.</span>"
/obj/machinery/atmospherics/components/binary/volume_pump/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
on = !on
update_icon()
investigate_log("Volume Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Volume Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/volume_pump/AltClick(mob/user)
if(can_interact(user))
transfer_rate = MAX_TRANSFER_RATE
investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [transfer_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/volume_pump/Destroy()
SSradio.remove_object(src,frequency)
return ..()
@@ -20,25 +20,19 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its flow rate.</span>"
/obj/machinery/atmospherics/components/trinary/filter/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
on = !on
update_icon()
investigate_log("Filter, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Filter, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/filter/AltClick(mob/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
transfer_rate = MAX_TRANSFER_RATE
to_chat(user,"<span class='notice'>You maximize the flow rate on the [src].</span>")
investigate_log("Filter, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Filter, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [transfer_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/filter/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
@@ -100,7 +94,10 @@
if(transfer_ratio > 0)
if(filter_type && air2.return_pressure() <= 9000)
air1.scrub_into(air2, transfer_ratio, list(filter_type))
if(filter_type in GLOB.gas_data.groups)
air1.scrub_into(air2, transfer_ratio, GLOB.gas_data.groups[filter_type])
else
air1.scrub_into(air2, transfer_ratio, list(filter_type))
if(air3.return_pressure() <= 9000)
air1.transfer_ratio_to(air3, transfer_ratio)
@@ -125,8 +122,10 @@
data["filter_types"] = list()
data["filter_types"] += list(list("name" = "Nothing", "id" = "", "selected" = !filter_type))
for(var/id in GLOB.gas_data.ids)
data["filter_types"] += list(list("name" = GLOB.gas_data.names[id], "id" = id, "selected" = (id == filter_type)))
if(!(id in GLOB.gas_data.groups_by_gas))
data["filter_types"] += list(list("name" = GLOB.gas_data.names[id], "id" = id, "selected" = (id == filter_type)))
for(var/group in GLOB.gas_data.groups)
data["filter_types"] += list(list("name" = group, "id" = group, "selected" = (group == filter_type)))
return data
/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params)
@@ -17,35 +17,27 @@
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(can_interact(user))
on = !on
update_icon()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
update_icon()
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output on set to [target_pressure] kPa")
update_appearance()
return ..()
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/update_icon()
cut_overlays()
/obj/machinery/atmospherics/components/trinary/mixer/update_overlays()
. = ..()
for(var/direction in GLOB.cardinals)
if(!(direction & initialize_directions))
continue
var/obj/machinery/atmospherics/node = findConnecting(direction)
var/image/cap
if(node)
cap = getpipeimage(icon, "cap", direction, node.pipe_color, piping_layer = piping_layer)
else
cap = getpipeimage(icon, "cap", direction, piping_layer = piping_layer)
add_overlay(cap)
return ..()
. += getpipeimage(icon, "cap", direction, pipe_color, piping_layer, TRUE)
/obj/machinery/atmospherics/components/trinary/mixer/update_icon_nopipes()
var/on_state = on && nodes[1] && nodes[2] && nodes[3] && is_operational()
@@ -72,10 +72,10 @@
QDEL_NULL(beaker)
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target)
/obj/machinery/atmospherics/components/unary/cryo_cell/contents_explosion(severity, target, origin)
..()
if(beaker)
beaker.ex_act(severity, target)
beaker.ex_act(severity, target, origin)
/obj/machinery/atmospherics/components/unary/cryo_cell/handle_atom_del(atom/A)
..()
@@ -439,7 +439,7 @@
return // we don't see the pipe network while inside cryo.
/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user)
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1)
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 1)
/obj/machinery/atmospherics/components/unary/cryo_cell/can_crawl_through()
return // can't ventcrawl in or out of cryo.
@@ -24,13 +24,16 @@
/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user)
if(can_interact(user))
on = !on
update_icon()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/unary/outlet_injector/AltClick(mob/user)
if(can_interact(user))
volume_rate = MAX_TRANSFER_RATE
update_icon()
investigate_log("was set to [volume_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [volume_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/unary/outlet_injector/Destroy()
@@ -59,10 +59,10 @@
/obj/machinery/atmospherics/components/unary/thermomachine/examine(mob/user)
. = ..()
. += "<span class='notice'>The thermostat is set to [target_temperature]K ([(T0C-target_temperature)*-1]C).</span>"
. += "<span class='notice'>The thermostat is set to [target_temperature]K ([target_temperature-T0C]C).</span>"
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Efficiency <b>[(heat_capacity/5000)*100]%</b>.</span>"
. += "<span class='notice'>Temperature range <b>[min_temperature]K - [max_temperature]K ([(T0C-min_temperature)*-1]C - [(T0C-max_temperature)*-1]C)</b>.</span>"
. += "<span class='notice'>The status display reads: Effective heat capacity <b>[heat_capacity] J/K</b>.</span>"
. += "<span class='notice'>Temperature range <b>[min_temperature]K - [max_temperature]K ([min_temperature-T0C]C - [max_temperature-T0C]C)</b>.</span>"
/obj/machinery/atmospherics/components/unary/thermomachine/process_atmos()
..()
@@ -217,16 +217,11 @@
min_temperature = max(T0C - (initial(min_temperature) + L * 15), TCMB) //73.15K with T1 stock parts
/obj/machinery/atmospherics/components/unary/thermomachine/freezer/AltClick(mob/living/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
if(!can_interact(user))
return
target_temperature = min_temperature
to_chat(user,"<span class='notice'>You minimize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "temperature reset to [target_temperature] K")
/obj/machinery/atmospherics/components/unary/thermomachine/heater
name = "heater"
@@ -178,6 +178,9 @@
var/mob/signal_sender = signal.data["user"]
if((("is_siphoning" in signal.data) && pump_direction == RELEASING) || (("is_pressurizing" in signal.data) && pump_direction == SIPHONING))
return
if("purge" in signal.data)
pressure_checks &= ~EXT_BOUND
pump_direction = SIPHONING
@@ -18,7 +18,8 @@
var/id_tag = null
var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing
var/filter_types = list(GAS_CO2)
var/filter_types = list(GAS_CO2, GAS_MIASMA, GAS_GROUP_CHEMICALS)
var/list/clean_filter_types = null
var/volume_rate = 200
var/widenet = 0 //is this scrubber acting on the 3x3 area around it.
var/list/turf/adjacent_turfs = list()
@@ -34,6 +35,16 @@
..()
if(!id_tag)
id_tag = assign_uid_vents()
generate_clean_filter_types()
RegisterSignal(SSdcs,COMSIG_GLOB_NEW_GAS,.proc/generate_clean_filter_types)
/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/generate_clean_filter_types()
clean_filter_types = list()
for(var/id in filter_types)
if(id in GLOB.gas_data.groups)
clean_filter_types += GLOB.gas_data.groups[id]
else
clean_filter_types += id
/obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy()
var/area/A = get_base_area(src)
@@ -95,7 +106,11 @@
var/list/f_types = list()
for(var/id in GLOB.gas_data.ids)
f_types += list(list("gas_id" = id, "gas_name" = GLOB.gas_data.names[id], "enabled" = (id in filter_types)))
if(!(id in GLOB.gas_data.groups_by_gas))
f_types += list(list("gas_id" = id, "gas_name" = GLOB.gas_data.names[id], "enabled" = (id in filter_types)))
for(var/group in GLOB.gas_data.groups)
f_types += list(list("gas_id" = group, "gas_name" = group, "enabled" = (group in filter_types)))
var/datum/signal/signal = new(list(
"tag" = id_tag,
@@ -147,11 +162,11 @@
var/datum/gas_mixture/environment = tile.return_air()
var/datum/gas_mixture/air_contents = airs[1]
if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE || !islist(filter_types))
if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE || !islist(clean_filter_types))
return FALSE
if(scrubbing & SCRUBBING)
environment.scrub_into(air_contents, volume_rate/environment.return_volume(), filter_types)
environment.scrub_into(air_contents, volume_rate/environment.return_volume(), clean_filter_types)
tile.air_update_turf()
@@ -205,11 +220,13 @@
if("toggle_filter" in signal.data)
filter_types ^= signal.data["toggle_filter"]
generate_clean_filter_types()
if("set_filters" in signal.data)
filter_types = list()
for(var/gas in signal.data["set_filters"])
filter_types += gas
generate_clean_filter_types()
if("init" in signal.data)
name = signal.data["init"]
+6
View File
@@ -33,6 +33,10 @@
var/ghost_usable = TRUE
var/skip_reentry_check = FALSE //Skips the ghost role blacklist time for people who ghost/suicide/cryo
///override this to add special spawn conditions to a ghost role
/obj/effect/mob_spawn/proc/allow_spawn(mob/user, silent = FALSE)
return TRUE
//ATTACK GHOST IGNORING PARENT RETURN VALUE
/obj/effect/mob_spawn/attack_ghost(mob/user, latejoinercalling)
if(!SSticker.HasRoundStarted() || !loc || !ghost_usable)
@@ -43,6 +47,8 @@
if(jobban_isbanned(user, banType))
to_chat(user, "<span class='warning'>You are jobanned!</span>")
return
if(!allow_spawn(user, silent = FALSE))
return
if(QDELETED(src) || QDELETED(user))
return
if(isobserver(user))
@@ -149,5 +149,5 @@ GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]")
if(prob(25))
mezzer()
/obj/singularity/narsie/mini/ex_act()
/obj/singularity/narsie/mini/ex_act(severity, target, origin)
return
@@ -0,0 +1,90 @@
#define BALLOON_TEXT_WIDTH 200
#define BALLOON_TEXT_SPAWN_TIME (0.2 SECONDS)
#define BALLOON_TEXT_FADE_TIME (0.1 SECONDS)
#define BALLOON_TEXT_FULLY_VISIBLE_TIME (0.7 SECONDS)
#define BALLOON_TEXT_TOTAL_LIFETIME(mult) (BALLOON_TEXT_SPAWN_TIME + BALLOON_TEXT_FULLY_VISIBLE_TIME*mult + BALLOON_TEXT_FADE_TIME)
/// The increase in duration per character in seconds
#define BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT (0.05)
/// The amount of characters needed before this increase takes into effect
#define BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN 10
/// Creates text that will float from the atom upwards to the viewer.
/atom/proc/balloon_alert(mob/viewer, text)
SHOULD_NOT_SLEEP(TRUE)
INVOKE_ASYNC(src, .proc/balloon_alert_perform, viewer, text)
/// Create balloon alerts (text that floats up) to everything within range.
/// Will only display to people who can see.
/atom/proc/balloon_alert_to_viewers(message, self_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
SHOULD_NOT_SLEEP(TRUE)
var/list/hearers = get_hearers_in_view(vision_distance, src)
hearers -= ignored_mobs
for (var/mob/hearer in hearers)
if (hearer.is_blind())
continue
balloon_alert(hearer, (hearer == src && self_message) || message)
// Do not use.
// MeasureText blocks. I have no idea for how long.
// I would've made the maptext_height update on its own, but I don't know
// if this would look bad on laggy clients.
/atom/proc/balloon_alert_perform(mob/viewer, text)
var/client/viewer_client = viewer.client
if (isnull(viewer_client))
return
var/bound_width = world.icon_size
if (ismovable(src))
var/atom/movable/movable_source = src
bound_width = movable_source.bound_width
var/image/balloon_alert = image(loc = get_atom_on_turf(src), layer = ABOVE_MOB_LAYER)
balloon_alert.plane = BALLOON_CHAT_PLANE
balloon_alert.alpha = 0
balloon_alert.appearance_flags = RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
balloon_alert.maptext = MAPTEXT("<span style='text-align: center; -dm-text-outline: 1px #0005'>[text]</span>")
balloon_alert.maptext_x = (BALLOON_TEXT_WIDTH - bound_width) * -0.5
balloon_alert.maptext_height = WXH_TO_HEIGHT(viewer_client?.MeasureText(text, null, BALLOON_TEXT_WIDTH))
balloon_alert.maptext_width = BALLOON_TEXT_WIDTH
viewer_client?.images += balloon_alert
var/duration_mult = 1
var/duration_length = length(text) - BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN
if(duration_length > 0)
duration_mult += duration_length*BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT
animate(
balloon_alert,
pixel_y = world.icon_size * 1.2,
time = BALLOON_TEXT_TOTAL_LIFETIME(1),
easing = SINE_EASING | EASE_OUT,
)
animate(
alpha = 255,
time = BALLOON_TEXT_SPAWN_TIME,
easing = CUBIC_EASING | EASE_OUT,
flags = ANIMATION_PARALLEL,
)
animate(
alpha = 0,
time = BALLOON_TEXT_FULLY_VISIBLE_TIME*duration_mult,
easing = CUBIC_EASING | EASE_IN,
)
addtimer(CALLBACK(GLOBAL_PROC, .proc/remove_image_from_client, balloon_alert, viewer_client), BALLOON_TEXT_TOTAL_LIFETIME(duration_mult))
#undef BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MIN
#undef BALLOON_TEXT_CHAR_LIFETIME_INCREASE_MULT
#undef BALLOON_TEXT_FADE_TIME
#undef BALLOON_TEXT_FULLY_VISIBLE_TIME
#undef BALLOON_TEXT_SPAWN_TIME
#undef BALLOON_TEXT_TOTAL_LIFETIME
#undef BALLOON_TEXT_WIDTH
@@ -0,0 +1,65 @@
GLOBAL_VAR_INIT(save_area_executing, FALSE)
/datum/mapGenerator/save_area
buildmode_name = "Save Area"
modules = list(/datum/mapGeneratorModule/save_area)
var/min_x = 0
var/min_y = 0
var/max_x = 0
var/max_y = 0
/datum/mapGenerator/save_area/defineRegion(turf/Start, turf/End, replace = 0)
min_x = min(Start.x,End.x)
min_y = min(Start.y,End.y)
max_x = max(Start.x,End.x)
max_y = max(Start.y,End.y)
..()
/datum/mapGeneratorModule/save_area
var/areaName = "default.dm"
//This could be optimised by making turfs that are the same go in the same, but this is a quick bodge solution so yea, fun job for coder here :)
/datum/mapGeneratorModule/save_area/generate()
var/datum/mapGenerator/save_area/L = mother
if(!istype(L))
return
//If someone somehow gets build mode, stop them from using this.
if(!check_rights(R_ADMIN))
message_admins("[ckey(usr)] tried to run the map save generator but was rejected due to insufficient perms.")
to_chat(usr, "<span class='warning'>You must have R_ADMIN privellages to use this.</span>")
return
//Emergency check
if(L.map.len > 1600)
var/confirm = alert("Uhm, are you sure, the area is quiet large?", "Run generator", "Yes", "No")
if(confirm != "Yes")
return
if(GLOB.save_area_executing)
to_chat(usr, "<span class='warning'>Someone is already running the generator! Try again in a bit.</span>")
return
to_chat(usr, "<span class='warning'>Saving, please wait...</span>")
GLOB.save_area_executing = TRUE
//Log just in case something happens
log_game("[key_name(usr)] ran the save level map generator on [L.map.len] turfs.")
message_admins("[key_name(usr)] ran the save level map generator on [L.map.len] turfs.")
//Step 1: Get the data (This can take a while)
var/dat = "//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE\n"
dat += convert_map_to_tgm(L.map)
//Step 2: Write the data to a file
var/filedir = file("data/temp.dmm")
if(fexists(filedir))
fdel(filedir)
WRITE_FILE(filedir, "[dat]")
//Step 3: Give the file to client for download
usr << ftp(filedir)
//Step 4: Remove the file from the server (hopefully we can find a way to avoid step)
fdel(filedir)
log_game("[L.map.len] turfs have been saved by [ckey(usr)]")
alert("Area saved successfully.", "Action Successful!", "Ok")
GLOB.save_area_executing = FALSE
+1 -1
View File
@@ -52,7 +52,7 @@
desc = "Packs of tools waiting to be used for repairing. Contains a tool and engineering vending machine refill. Requires CE access."
cost = 5500 //Powerfull
access = ACCESS_CE
contains = list(/obj/item/vending_refill/tool,
contains = list(/obj/item/vending_refill/youtool,
/obj/item/vending_refill/engivend)
crate_name = "engineering supply crate"
crate_type = /obj/structure/closet/crate/secure/engineering
+2 -2
View File
@@ -187,10 +187,10 @@
else
..()
/obj/structure/closet/supplypod/ex_act() //Explosions dont do SHIT TO US! This is because supplypods create explosions when they land.
/obj/structure/closet/supplypod/ex_act(severity, target, origin) //Explosions dont do SHIT TO US! This is because supplypods create explosions when they land.
return
/obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding.
/obj/structure/closet/supplypod/contents_explosion(severity, target, origin) //Supplypods also protect their contents from the harmful effects of fucking exploding.
return
/obj/structure/closet/supplypod/toggle(mob/living/user)
+13 -14
View File
@@ -4,6 +4,16 @@
//BLACK MAGIC THINGS//
//////////////////////
parent_type = /datum
///////////////
// Rendering //
///////////////
/// Click catcher
var/atom/movable/screen/click_catcher/click_catcher
/// Parallax holder
var/datum/parallax_holder/parallax_holder
////////////////
//ADMIN THINGS//
////////////////
@@ -34,6 +44,9 @@
var/last_move = 0
var/area = null
/// Timers are now handled by clients, not by doing a mess on the item and multiple people overwriting a single timer on the object, have fun.
var/tip_timer = null
/// Last time we Click()ed. No clicking twice in one tick!
var/last_click = 0
@@ -150,20 +163,6 @@
///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns
var/cryo_warned = -5 MINUTES
var/list/parallax_layers
var/list/parallax_layers_cached
var/atom/movable/movingmob
var/turf/previous_turf
///world.time of when we can state animate()ing parallax again
var/dont_animate_parallax
///world.time of last parallax update
var/last_parallax_shift
///ds between parallax updates
var/parallax_throttle = 0
var/parallax_movedir = 0
var/parallax_layers_max = 3
var/parallax_animate_timer
/**
* Assoc list with all the active maps - when a screen obj is added to
* a map, it's put in here as well.
+54 -41
View File
@@ -79,15 +79,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
// Tgui Topic middleware
if(tgui_Topic(href_list))
if(CONFIG_GET(flag/emergency_tgui_logging))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
return
if(href_list["reload_tguipanel"])
nuke_chat()
if(href_list["reload_statbrowser"])
src << browse(file('html/statbrowser.html'), "window=statbrowser")
// Log all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
last_activity = world.time
//Logs all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
//byond bug ID:2256651
if (asset_cache_job && (asset_cache_job in completed_asset_jobs))
to_chat(src, "<span class='danger'>An error has been detected in how your client is receiving resources. Attempting to correct.... (If you keep seeing these messages you might want to close byond and reconnect)</span>")
@@ -353,13 +354,19 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
// Initialize tgui panel
tgui_panel.initialize()
src << browse(file('html/statbrowser.html'), "window=statbrowser")
addtimer(CALLBACK(src, .proc/check_panel_loaded), 30 SECONDS)
tgui_panel.initialize()
if(alert_mob_dupe_login)
spawn()
alert(mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
if(alert_mob_dupe_login && !holder)
var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!"
// if (alert_admin_multikey)
// dupe_login_message += "\nAdmins have been informed."
// message_admins(span_danger("<B>MULTIKEYING: </B></span><span class='notice'>[key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned."))
// log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")
spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer
alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please.
to_chat(mob, span_danger(dupe_login_message))
connection_time = world.time
connection_realtime = world.realtime
@@ -437,8 +444,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
send_resources()
generate_clickcatcher()
apply_clickcatcher()
update_clickcatcher()
if(prefs.lastchangelog != GLOB.changelog_hash) //bolds the changelog button on the interface so we know there are updates.
to_chat(src, "<span class='info'>You have unread updates in the changelog.</span>")
@@ -528,9 +534,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
send2adminchat("Server", "[cheesy_message] (No admins online)")
QDEL_LIST_ASSOC_VAL(char_render_holders)
if(movingmob != null)
movingmob.client_mobs_in_contents -= mob
UNSETEMPTY(movingmob.client_mobs_in_contents)
// seen_messages = null
Master.UpdateTickRate()
. = ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
@@ -1007,7 +1010,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/list/old_view = getviewsize(view)
view = new_size
var/list/actualview = getviewsize(view)
apply_clickcatcher(actualview)
update_clickcatcher()
parallax_holder.Reset()
mob.reload_fullscreen()
if (isliving(mob))
var/mob/living/M = mob
@@ -1016,33 +1020,31 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
addtimer(CALLBACK(src,.verb/fit_viewport,10)) //Delayed to avoid wingets from Login calls.
SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_CHANGE_VIEW, src, old_view, actualview)
/client/proc/generate_clickcatcher()
if(!void)
void = new()
screen += void
/client/proc/apply_clickcatcher(list/actualview)
generate_clickcatcher()
if(!actualview)
actualview = getviewsize(view)
void.UpdateGreed(actualview[1],actualview[2])
/client/proc/AnnouncePR(announcement)
if(prefs && prefs.chat_toggles & CHAT_PULLR)
to_chat(src, announcement)
/client/proc/show_character_previews(mutable_appearance/MA)
/client/proc/show_character_previews(mutable_appearance/source)
LAZYINITLIST(char_render_holders)
if(!LAZYLEN(char_render_holders))
for(var/plane_master_path as anything in subtypesof(/atom/movable/screen/plane_master))
var/atom/movable/screen/plane_master/plane_master = new plane_master_path()
char_render_holders["plane_master-[plane_master.plane]"] = plane_master
plane_master.backdrop(mob)
screen |= plane_master
plane_master.screen_loc = "character_preview_map:0,CENTER"
var/pos = 0
for(var/D in GLOB.cardinals)
for(var/dir in GLOB.cardinals)
pos++
var/atom/movable/screen/O = LAZYACCESS(char_render_holders, "[D]")
if(!O)
O = new
LAZYSET(char_render_holders, "[D]", O)
screen |= O
O.appearance = MA
O.dir = D
O.screen_loc = "character_preview_map:0,[pos]"
var/atom/movable/screen/preview = char_render_holders["preview-[dir]"]
if(!preview)
preview = new
char_render_holders["preview-[dir]"] = preview
screen |= preview
preview.appearance = source
preview.dir = dir
preview.screen_loc = "character_preview_map:0,[pos]"
/client/proc/clear_character_previews()
for(var/index in char_render_holders)
@@ -1078,8 +1080,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(IsAdminAdvancedProcCall())
return
var/list/verblist = list()
verb_tabs.Cut()
for(var/thing in (verbs + mob?.verbs))
var/list/verbstoprocess = verbs.Copy()
if(mob)
verbstoprocess += mob.verbs
for(var/AM in mob.contents)
var/atom/movable/thing = AM
verbstoprocess += thing.verbs
panel_tabs.Cut() // panel_tabs get reset in init_verbs on JS side anyway
for(var/thing in verbstoprocess)
var/procpath/verb_to_init = thing
if(!verb_to_init)
continue
@@ -1087,9 +1095,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
continue
if(!istext(verb_to_init.category))
continue
verb_tabs |= verb_to_init.category
panel_tabs |= verb_to_init.category
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
src << output("[url_encode(json_encode(panel_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
/client/proc/check_panel_loaded()
if(statbrowser_ready)
return
to_chat(src, span_userdanger("Statpanel failed to load, click <a href='?src=[REF(src)];reload_statbrowser=1'>here</a> to reload the panel "))
//increment progress for an unlockable loadout item
/client/proc/increment_progress(key, amount)
+14 -11
View File
@@ -61,7 +61,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/UI_style = null
var/outline_enabled = TRUE
var/outline_color = COLOR_BLUE_GRAY
var/outline_color = COLOR_THEME_MIDNIGHT
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -164,13 +164,15 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/clientfps = 0
var/parallax
var/parallax = PARALLAX_INSANE
var/ambientocclusion = TRUE
///Should we automatically fit the viewport?
var/auto_fit_viewport = FALSE
///Should we be in the widescreen mode set by the config?
var/widescreenpref = TRUE
///Strip menu style
var/long_strip_menu = FALSE
///What size should pixels be displayed as? 0 is strech to fit
var/pixel_size = 0
///What scaling method should we use?
@@ -785,7 +787,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<h2>General Settings</h2>"
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
dat += "<b>Outline:</b> <a href='?_src_=prefs;preference=outline_enabled'>[outline_enabled ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Outline Color:</b> <span style='border:1px solid #161616; background-color: [outline_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
dat += "<b>Outline Color:</b> [outline_color ? "<span style='border:1px solid #161616; background-color: [outline_color];'>" : "Theme-based (null)"]&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
@@ -829,6 +831,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat +="<td width='300px' height='300px' valign='top'>"
dat += "<h2>Citadel Preferences</h2>" //Because fuck me if preferences can't be fucking modularized and expected to update in a reasonable timeframe.
dat += "<b>Widescreen:</b> <a href='?_src_=prefs;preference=widescreenpref'>[widescreenpref ? "Enabled ([CONFIG_GET(string/default_view)])" : "Disabled (15x15)"]</a><br>"
dat += "<b>Long strip menu:</b> <a href='?_src_=prefs;preference=long_strip_menu'>[long_strip_menu ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Auto stand:</b> <a href='?_src_=prefs;preference=autostand'>[autostand ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Auto OOC:</b> <a href='?_src_=prefs;preference=auto_ooc'>[auto_ooc ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Force Slot Storage HUD:</b> <a href='?_src_=prefs;preference=no_tetris_storage'>[no_tetris_storage ? "Enabled" : "Disabled"]</a><br>"
@@ -2591,6 +2594,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("widescreenpref")
widescreenpref = !widescreenpref
user.client.view_size.setDefault(getScreenSize(widescreenpref))
if("long_strip_menu")
long_strip_menu = !long_strip_menu
if("pixel_size")
switch(pixel_size)
@@ -2739,8 +2744,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
outline_enabled = !outline_enabled
if("outline_color")
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
if(pickedOutlineColor)
outline_color = pickedOutlineColor
if(pickedOutlineColor != pickedOutlineColor)
outline_color = pickedOutlineColor // nullable
if("tgui_lock")
tgui_lock = !tgui_lock
if("winflash")
@@ -2811,14 +2816,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
toggles ^= MIDROUND_ANTAG
if("parallaxup")
parallax = WRAP(parallax + 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
if (parent && parent.mob && parent.mob.hud_used)
parent.mob.hud_used.update_parallax_pref(parent.mob)
parallax = WRAP(parallax + 1, PARALLAX_DISABLE, PARALLAX_INSANE + 1)
parent?.parallax_holder?.Reset()
if("parallaxdown")
parallax = WRAP(parallax - 1, PARALLAX_INSANE, PARALLAX_DISABLE + 1)
if (parent && parent.mob && parent.mob.hud_used)
parent.mob.hud_used.update_parallax_pref(parent.mob)
parallax = WRAP(parallax - 1, PARALLAX_DISABLE, PARALLAX_INSANE + 1)
parent?.parallax_holder?.Reset()
// Citadel edit - Prefs don't work outside of this. :c
+9 -3
View File
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
#define SAVEFILE_VERSION_MAX 52
#define SAVEFILE_VERSION_MAX 53
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -47,7 +47,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
if(current_version < 30)
outline_enabled = TRUE
outline_color = COLOR_BLUE_GRAY
outline_color = COLOR_THEME_MIDNIGHT
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 19)
@@ -343,6 +343,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
marking_list += list(list(part, old_marking_value, copied_color_list))
features["mam_body_markings"] = marking_list
if(current_version < 53)
parallax = PARALLAX_INSANE
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
@@ -410,6 +413,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["ambientocclusion"] >> ambientocclusion
S["auto_fit_viewport"] >> auto_fit_viewport
S["widescreenpref"] >> widescreenpref
S["long_strip_menu"] >> long_strip_menu
S["pixel_size"] >> pixel_size
S["scaling_method"] >> scaling_method
S["hud_toggle_flash"] >> hud_toggle_flash
@@ -468,10 +472,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
toggles = sanitize_integer(toggles, 0, 16777215, initial(toggles))
clientfps = sanitize_integer(clientfps, 0, 1000, 0)
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
parallax = sanitize_integer(parallax, PARALLAX_DISABLE, PARALLAX_INSANE, null)
ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref))
long_strip_menu = sanitize_integer(long_strip_menu, 0, 1, initial(long_strip_menu))
pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size))
scaling_method = sanitize_text(scaling_method, initial(scaling_method))
hud_toggle_flash = sanitize_integer(hud_toggle_flash, 0, 1, initial(hud_toggle_flash))
@@ -604,6 +609,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["damagescreenshake"], damagescreenshake)
WRITE_FILE(S["arousable"], arousable)
WRITE_FILE(S["widescreenpref"], widescreenpref)
WRITE_FILE(S["long_strip_menu"], long_strip_menu)
WRITE_FILE(S["autostand"], autostand)
WRITE_FILE(S["cit_toggles"], cit_toggles)
WRITE_FILE(S["preferred_chaos"], preferred_chaos)
+9
View File
@@ -18,3 +18,12 @@
prefs.tip_delay = max(indelay, 0.01)
prefs.save_preferences()
to_chat(usr, "<span class='danger'>Tooltip delay set to [indelay] milliseconds.</span>")
/client/verb/toggle_hover_outline()
set name = "Toggle hover outline"
set desc = "Toggles hover-over item outline"
set category = "Preferences"
prefs.outline_enabled = !prefs.outline_enabled
prefs.save_preferences()
to_chat(usr, "<span class='danger'>Item outline [prefs.outline_enabled ? "en" : "dis"]abled.</span>")
+1 -3
View File
@@ -21,8 +21,6 @@
var/active_sound = null
var/toggle_cooldown = null
var/cooldown = 0
var/obj/item/flashlight/F = null
var/can_flashlight = 0
var/blocks_shove_knockdown = FALSE //Whether wearing the clothing item blocks the ability for shove to knock down.
@@ -445,7 +443,7 @@ BLIND // can't see anything
..()
//Species-restricted clothing check. - Thanks Oraclestation, BS13, /vg/station etc.
/obj/item/clothing/mob_can_equip(mob/M, slot, disable_warning = TRUE)
/obj/item/clothing/mob_can_equip(M, equipper, slot, disable_warning = TRUE, bypass_equip_delay_self)
//if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam)
if(!..())
+30
View File
@@ -0,0 +1,30 @@
/obj/item/clothing/gloves/mittens
desc = "These gloves will keep your hands warm!"
name = "mittens"
icon_state = "mittens"
item_state = "wgloves"
//item_color = "white"
cold_protection = HANDS
min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT
resistance_flags = NONE
/obj/item/clothing/gloves/mittens/random
/obj/item/clothing/gloves/mittens/random/Initialize()
..()
var/colours = list("black", "yellow", "lightbrown", "brown", "orange", "red", "purple", "green", "blue", "kitten")
var/picked_c = pick(colours)
if(picked_c == "kitten")
new /obj/item/clothing/gloves/mittens/kitten(loc)
qdel(src)
return INITIALIZE_HINT_QDEL
item_state = "[picked_c]gloves"
//item_color = "[picked_c]"
color = picked_c
/obj/item/clothing/gloves/mittens/kitten
name = "Kitten mittens"
desc = "These gloves will keep your hands warm, and feature cute kittens"
icon_state = "kittenmittens"
item_state = "blackgloves"
//item_color = "black"
+70 -40
View File
@@ -16,17 +16,47 @@
dog_fashion = /datum/dog_fashion/head/helmet
var/can_flashlight = FALSE //if a flashlight can be mounted. if it has a flashlight and this is false, it is permanently attached.
var/obj/item/flashlight/seclite/attached_light
var/datum/action/item_action/toggle_helmet_flashlight/alight
/obj/item/clothing/head/helmet/Initialize(mapload)
. = ..()
if(attached_light)
alight = new(src)
/obj/item/clothing/head/helmet/ComponentInitialize()
. = ..()
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_HEAD))
/obj/item/clothing/head/helmet/examine(mob/user)
. = ..()
if(attached_light)
. += "It has \a [attached_light] [can_flashlight ? "" : "permanently "]mounted on it."
if(can_flashlight)
. += "<span class='info'>[attached_light] looks like it can be <b>unscrewed</b> from [src].</span>"
else if(can_flashlight)
. += "It has a mounting point for a <b>seclite</b>."
/obj/item/clothing/head/helmet/Destroy()
QDEL_NULL(attached_light)
return ..()
/obj/item/clothing/head/helmet/handle_atom_del(atom/A)
if(A == attached_light)
attached_light = null
update_helmlight()
update_icon()
QDEL_NULL(alight)
return ..()
/obj/item/clothing/head/helmet/sec
can_flashlight = 1
/obj/item/clothing/head/helmet/sec/attackby(obj/item/I, mob/user, params)
if(issignaler(I))
var/obj/item/assembly/signaler/S = I
if(F) //Has a flashlight. Player must remove it, else it will be lost forever.
if(attached_light) //Has a flashlight. Player must remove it, else it will be lost forever.
to_chat(user, "<span class='warning'>The mounted flashlight is in the way, remove it first!</span>")
return
@@ -280,8 +310,8 @@
/obj/item/clothing/head/helmet/update_icon_state()
var/state = "[initial(icon_state)]"
if(F)
if(F.on)
if(attached_light)
if(attached_light.on)
state += "-flight-on" //"helmet-flight-on" // "helmet-cam-flight-on"
else
state += "-flight" //etc.
@@ -289,7 +319,7 @@
icon_state = state
/obj/item/clothing/head/helmet/ui_action_click(mob/user, action)
if(istype(action, /datum/action/item_action/toggle_helmet_flashlight))
if(istype(action, alight))
toggle_helmlight()
else
..()
@@ -297,60 +327,60 @@
/obj/item/clothing/head/helmet/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/flashlight/seclite))
var/obj/item/flashlight/seclite/S = I
if(can_flashlight)
if(!F)
if(!user.transferItemToLoc(S, src))
return
to_chat(user, "<span class='notice'>You click [S] into place on [src].</span>")
if(S.on)
set_light(0)
F = S
update_icon()
update_helmlight(user)
verbs += /obj/item/clothing/head/helmet/proc/toggle_helmlight
var/datum/action/A = new /datum/action/item_action/toggle_helmet_flashlight(src)
if(loc == user)
A.Grant(user)
if(can_flashlight && !attached_light)
if(!user.transferItemToLoc(S, src))
return
to_chat(user, "<span class='notice'>You click [S] into place on [src].</span>")
if(S.on)
set_light(0)
attached_light = S
update_icon()
update_helmlight()
alight = new(src)
if(loc == user)
alight.Grant(user)
return
if(I.tool_behaviour == TOOL_SCREWDRIVER)
if(F)
for(var/obj/item/flashlight/seclite/S in src)
to_chat(user, "<span class='notice'>You unscrew the seclite from [src].</span>")
F = null
S.forceMove(user.drop_location())
update_helmlight(user)
S.update_brightness(user)
update_icon()
usr.update_inv_head()
verbs -= /obj/item/clothing/head/helmet/proc/toggle_helmlight
for(var/datum/action/item_action/toggle_helmet_flashlight/THL in actions)
qdel(THL)
return
return ..()
/obj/item/clothing/head/helmet/screwdriver_act(mob/living/user, obj/item/I)
..()
if(can_flashlight && attached_light) //if it has a light but can_flashlight is false, the light is permanently attached.
I.play_tool_sound(src)
to_chat(user, "<span class='notice'>You unscrew [attached_light] from [src].</span>")
attached_light.forceMove(drop_location())
if(Adjacent(user) && !issilicon(user))
user.put_in_hands(attached_light)
var/obj/item/flashlight/removed_light = attached_light
attached_light = null
update_helmlight()
removed_light.update_brightness(user)
update_icon()
user.update_inv_head()
QDEL_NULL(alight)
return TRUE
/obj/item/clothing/head/helmet/proc/toggle_helmlight()
set name = "Toggle Helmetlight"
set category = "Object"
set desc = "Click to toggle your helmet's attached flashlight."
if(!F)
if(!attached_light)
return
var/mob/user = usr
if(user.incapacitated())
return
F.on = !F.on
to_chat(user, "<span class='notice'>You toggle the helmetlight [F.on ? "on":"off"].</span>")
attached_light.on = !attached_light.on
to_chat(user, "<span class='notice'>You toggle the helmet-light [attached_light.on ? "on":"off"].</span>")
playsound(user, 'sound/weapons/empty.ogg', 100, 1)
update_helmlight(user)
/obj/item/clothing/head/helmet/proc/update_helmlight(mob/user = null)
if(F)
if(F.on)
set_light(F.brightness_on, F.flashlight_power, F.light_color)
if(attached_light)
if(attached_light.on)
set_light(attached_light.brightness_on, attached_light.flashlight_power, attached_light.light_color)
else
set_light(0)
update_icon()
@@ -267,9 +267,15 @@
desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement."
icon_state = "jetboots"
resistance_flags = FIRE_PROOF
cold_protection = FEET|LEGS
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET|LEGS
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
actions_types = list(/datum/action/item_action/bhop)
permeability_coefficient = 0.05
unique_reskin = list("Explorer" = "miningjet",
"Jackboot" = "jetboots")
var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles
var/jumpspeed = 3
var/recharging_rate = 60 //default 6 seconds between each dash
+6 -2
View File
@@ -20,12 +20,16 @@
/obj/item/clothing/head/helmet/space/hardsuit/Initialize()
. = ..()
soundloop = new(list(), FALSE, TRUE)
soundloop = new(src, FALSE, TRUE)
soundloop.volume = 5
START_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/Destroy()
. = ..()
if(!QDELETED(suit))
qdel(suit)
suit = null
QDEL_NULL(soundloop)
STOP_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user)
@@ -633,7 +637,7 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/clown
mutantrace_variation = STYLE_DIGITIGRADE
/obj/item/clothing/suit/space/hardsuit/clown/mob_can_equip(mob/M, slot)
/obj/item/clothing/suit/space/hardsuit/clown/mob_can_equip(mob/M, equipper, slot, disable_warning, bypass_equip_delay_self)
if(!..() || !ishuman(M))
return FALSE
if(M.mind && HAS_TRAIT(M.mind, TRAIT_CLOWN_MENTALITY))
+1 -1
View File
@@ -53,5 +53,5 @@
desc = "An ironically skimpy blue dress with gold markings denoting the rank of \"Captain\"."
icon_state = "lewdcap"
item_state = "lewdcap"
can_adjust = FALSE
alt_covers_chest = TRUE
mutantrace_variation = STYLE_DIGITIGRADE|USE_TAUR_CLIP_MASK
+1 -1
View File
@@ -1,4 +1,4 @@
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 10
#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME 30
//this datum is used by the events controller to dictate how it selects events
/datum/round_event_control
+23
View File
@@ -0,0 +1,23 @@
/datum/round_event_control/atmos_flux
name = "Atmospheric Flux"
typepath = /datum/round_event/atmos_flux
max_occurrences = 1
weight = 5
endWhen = 600
var/original_speed
/datum/round_event/atmos_flux
announceWhen = 1
/datum/round_event/atmos_flux/announce(fake)
priority_announce("Atmospheric flux in your sector detected. Sensors show that air may move [(SSair.share_max_steps_target > original_speed) ? "faster" : "slower"] than usual for some time.", "Atmos Alert")
/datum/round_event/atmos_flux/start()
original_speed = SSair.share_max_steps_target
if(prob(20))
SSair.share_max_steps_target = max(1, original_speed - rand(1,original_speed-1))
else
SSair.share_max_steps_target += rand(2,10)
/datum/round_event/atmos_flux/end()
SSair.share_max_steps_target = original_speed
+1 -1
View File
@@ -99,7 +99,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
walk(src,0)
walk_towards(src, destination, 1)
/obj/effect/immovablerod/ex_act(severity, target)
/obj/effect/immovablerod/ex_act(severity, target, origin)
return 0
/obj/effect/immovablerod/singularity_act()
+1 -1
View File
@@ -507,7 +507,7 @@
if(master)
master.spawn_spacevine_piece(stepturf, src)
/obj/structure/spacevine/ex_act(severity, target)
/obj/structure/spacevine/ex_act(severity, target, origin)
if(istype(target, type)) //if its agressive spread vine dont do anything
return
var/i
+2 -2
View File
@@ -58,7 +58,7 @@
supernova.power_mod = min(supernova.power_mod*1.2, power)
if(activeFor > endWhen-10)
supernova.power_mod /= 4
if(prob(round(supernova.power_mod)) && prob(3) && storm_count < 5 && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
if(prob(round(supernova.power_mod)) && prob(5-storm_count) && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
SSweather.run_weather(/datum/weather/rad_storm/supernova)
storm_count++
@@ -73,6 +73,6 @@
weather_duration_lower = 50
weather_duration_upper = 100
telegraph_duration = 200
radiation_intensity = 1000
radiation_intensity = 500
weather_sound = null
telegraph_message = "<span class='userdanger'>The air begins to grow very warm!</span>"
@@ -57,7 +57,11 @@ God bless America.
component_parts += new /obj/item/circuitboard/machine/deep_fryer(null)
component_parts += new /obj/item/stock_parts/micro_laser(null)
RefreshParts()
fry_loop = new(list(src), FALSE)
fry_loop = new(src, FALSE)
/obj/machinery/deepfryer/Destroy()
QDEL_NULL(fry_loop)
return ..()
/obj/machinery/deepfryer/RefreshParts()
var/oil_efficiency
@@ -13,15 +13,21 @@
/obj/machinery/grill/Initialize()
. = ..()
grill_loop = new(list(src), FALSE)
grill_loop = new(src, FALSE)
/obj/machinery/grill/Destroy()
QDEL_NULL(grill_loop)
return ..()
/obj/machinery/grill/update_icon_state()
if(grilled_item)
icon_state = "grill"
else if(grill_fuel)
return ..()
if(grill_fuel > 0)
icon_state = "grill_on"
else
icon_state = "grill_open"
return ..()
icon_state = "grill_open"
return ..()
/obj/machinery/grill/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/sheet/mineral/coal) || istype(I, /obj/item/stack/sheet/mineral/wood))
@@ -60,21 +66,21 @@
return
..()
/obj/machinery/grill/process()
/obj/machinery/grill/process(delta_time)
..()
update_icon()
if(!grill_fuel)
update_appearance()
if(grill_fuel <= 0)
return
else
grill_fuel -= 1
if(prob(1))
grill_fuel -= 0.5 * delta_time
if(DT_PROB(0.5, delta_time))
var/datum/effect_system/smoke_spread/bad/smoke = new
smoke.set_up(1, loc)
smoke.start()
if(grilled_item)
grill_time += 1
grill_time += delta_time
grilled_item.reagents.add_reagent("char", 1)
grill_fuel -= 10
grill_fuel -= 5 * delta_time
grilled_item.AddComponent(/datum/component/sizzle)
/obj/machinery/grill/Exited(atom/movable/AM)
@@ -109,9 +115,9 @@
/obj/machinery/grill/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(grilled_item)
to_chat(user, "<span class='notice'>You take out [grilled_item] from [src].</span>")
to_chat(user, span_notice("You take out [grilled_item] from [src]."))
grilled_item.forceMove(drop_location())
update_icon()
update_appearance()
return
return ..()
@@ -36,12 +36,13 @@
. = ..()
wires = new /datum/wires/microwave(src)
create_reagents(100)
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/machinery/microwave/Destroy()
eject()
if(wires)
QDEL_NULL(wires)
QDEL_NULL(soundloop)
. = ..()
/obj/machinery/microwave/RefreshParts()
@@ -10,8 +10,12 @@
speech_span = "spooky"
resistance_flags = INDESTRUCTIBLE
var/active = TRUE
var/BallTutorial = FALSE
/obj/item/barthpot/attackby(obj/item/I, mob/user, params)
if(BallTutorial)
say("Hello and welcome to the annual Citadelstation Spookyball 2021! CENTCOM requisitioned this old hospital as a new colony site two years ago, and were back again after a few additions here and there. Next to me you can find all the tools youll need to build a nice private house, if youre here for that kind of thing. The axes will chop trees, and give you wood. Shovels will remove grass and things, which you can use the grass to make beds and backets. The pickaxe will allow you pick out the nearby rocks, if youre more interested in building a cave dwelling. Finally the umbrella and light sources are because its spooky, and it might rain! As for interesting spots; Theres the old abandoned mansion which you can get to by going through the entrance and towards the east. Theres also a mech arena directly south, and a racetrack in the caves to the right of the arena, follow the lanterns. And make sure to explore the caves too! Lots of neat things to find!")
return
if(!active)
say("Meow!")
return
@@ -27,6 +31,9 @@
say("It doesn't seem like that's magical enough!")
/obj/item/barthpot/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(BallTutorial)
say("Hello and welcome to the annual Citadelstation Spookyball 2021! CENTCOM requisitioned this old hospital as a new colony site two years ago, and were back again after a few additions here and there. Next to me you can find all the tools youll need to build a nice private house, if youre here for that kind of thing. The axes will chop trees, and give you wood. Shovels will remove grass and things, which you can use the grass to make beds and backets. The pickaxe will allow you pick out the nearby rocks, if youre more interested in building a cave dwelling. Finally the umbrella and light sources are because its spooky, and it might rain! As for interesting spots; Theres the old abandoned mansion which you can get to by going through the entrance and towards the east. Theres also a mech arena directly south, and a racetrack in the caves to the right of the arena, follow the lanterns. And make sure to explore the caves too! Lots of neat things to find!")
return
if(!active)
say("Meow!")
return
@@ -268,3 +268,65 @@
category = "Holiday"
item = /obj/item/card/emag/halloween
surplus = 0
/////////////////////////
// Ball map Items //
/////////////////////////
/obj/item/wisp_lantern/pumpkin
name = "Pumpkin lantern"
desc = "This lantern gives off no light, but is home to a friendly Jacq o' latern."
icon_state = "lantern-on"
var/obj/effect/wisp/pumpkin/wisp2
//Hoooo boy that's some wild code there.
/obj/item/wisp_lantern/pumpkin/Initialize()
. = ..()
qdel(wisp)
wisp2 = new(src)
wisp = wisp2
/obj/effect/wisp/pumpkin
name = "Friendly pumpkin"
desc = "Happy to spook your way."
icon = 'icons/obj/clothing/hats.dmi'
icon_state = "hardhat1_pumpkin_j"
light_range = 5
light_color = "#EE9933"
layer = 0
sight_flags = SEE_MOBS
lighting_alpha = 0
/obj/effect/wisp/pumpkin/update_user_sight(mob/user) //Disables SUPERLIGHTS
return
//Damnit LazyBones
/mob/living/simple_animal/lazy_bones
name = "Lazy Bones"
desc = "Simply refuses to get out of bed!"
icon = 'icons/mob/simple_human.dmi'
icon_state = "skeleton"
icon_living = "skeleton"
icon_dead = "skeleton"
gender = NEUTER
mob_biotypes = list(MOB_UNDEAD, MOB_HUMANOID)
turns_per_move = 5
speak_emote = list("rattles")
emote_see = list("rattles")
maxHealth = 40
health = 40
speed = 0
harm_intent_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
minbodytemp = 0
maxbodytemp = 1500
faction = list("skeleton")
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
deathmessage = "collapses into a pile of bones!"
del_on_death = 1
loot = list(/obj/effect/decal/remains/human)
stop_automated_movement = 1
+182 -2
View File
@@ -50,6 +50,7 @@
var/cached_z
/// I'm busy, don't move.
var/busy = FALSE
var/spawn_cars = FALSE
var/static/blacklisted_items = typecacheof(list(
/obj/effect,
@@ -91,6 +92,9 @@
poof()
/mob/living/simple_animal/jacq/on_attack_hand(mob/living/carbon/human/M)
if(spawn_cars)
spawn_cars(M)
return ..()
if(!active)
say("Hello there [gender_check(M)]!")
return ..()
@@ -101,6 +105,9 @@
..()
/mob/living/simple_animal/jacq/attack_paw(mob/living/carbon/monkey/M)
if(spawn_cars)
spawn_cars(M)
return ..()
if(!active)
say("Hello there [gender_check(M)]!")
return ..()
@@ -128,6 +135,8 @@
/mob/living/simple_animal/jacq/proc/poof()
if(!active)//if disabled, don't poof
return
last_poof = world.realtime
var/datum/reagents/R = new/datum/reagents(100)//Hey, just in case.
var/datum/effect_system/smoke_spread/chem/s = new()
@@ -145,7 +154,7 @@
pop_areas += A
var/list/targets = list()
for(var/H in GLOB.network_holopads)
for(var/H in GLOB.the_station_areas)
var/area/A = get_area(H)
if(!A || findtextEx(A.name, "AI") || !(A in pop_areas) || !is_station_level(H))
continue
@@ -470,6 +479,30 @@
sleep(20)
poof()
/mob/living/simple_animal/jacq/proc/spawn_cars(mob/living/carbon/C)
visible_message("<b>[src]</b> gives off a glowing smile, <span class='spooky'>\"What ken Ah offer ye? I can magic up a vectorcraft in manual or automatic fer ye.\"</span>")
var/choices_reward = list("Manual", "Automatic", "How do Automatics work?", "Nothing, thanks")
var/choice_reward = input(usr, "Trick or Treat?", "Trick or Treat?") in choices_reward
switch(choice_reward)
if("Manual")
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"Great choice! 'Ere's yer car.\"</span>")
jacqrunes("Great choice! 'Ere's yer car.", C)
new /obj/vehicle/sealed/vectorcraft(loc)
if("Automatic")
visible_message("<b>[src]</b> waves their arms around, <span class='spooky'>\"'Ere's yer car. Not as fast as an automatic mind.\"</span>")
jacqrunes("'Ere's yer car. Not as fast as an automatic mind.", C)
new /obj/vehicle/sealed/vectorcraft/auto(loc)
if("How do Automatics work?")
visible_message("<b>[src]</b> smiles, <span class='spooky'>\"Hold wasd to gain speed in a direction, c to enable/disable the clutch, 1 2 3 4 to change gears (help is gear 1, disarm is gear 2, grab is gear 3 and harm is gear 4) while holding a direction (make sure the clutch is enabled when you change gears, you should hear a sound when you've successfully changed gears), r to toggle handbrake, hold alt for brake and press shift for boost (the machine will beep when the boost is recharged)! If you hear an ebbing sound like \"brbrbrbrbr\" you need to gear down, the whining sound means you need to gear up. Hearing a pleasant \"whumwhumwhum\" is optimal gearage! It can be a lil slow to start, so make sure you're in the 1st gear, andusing a boost to get you started is a good idea. If you've got a good speed you'll likely never need to dip down to gear 1 again, and make sure to hold the acceleration pedal down while changing gears (hold a direction). 1st gear is for slow movement, and it's a good idea to mvoe to 2nd gear as quick as you can, you can coldstart a car from gear one by slowly moving, then using the boost to jump you up to gear 2 speeds. The upper gears are for unlimiting your top speed.\"</span>")
jacqrunes("They're a bit tricky, aye. Basically;", C)
if("Nothing, thanks")
visible_message("<b>[src]</b> shrugs, <span class='spooky'>\"Suit yerself.\"</span>")
jacqrunes("Suit yerself.", C)
visible_message("<b>[src]</b> shrugs, <span class='spooky'>\"Oh and look after the crafts, aye? They can get a wee bit... explosive if banged up a tad too much. They move slower damaged too like. Ye can repair 'em with the welders o'er there.\"</span>")
jacqrunes("Oh and look after the crafts, aye? They can get a wee bit... explosive if banged up a tad too much. They move slower damaged too like. Ye can repair 'em with the welders o'er there. ", C)
/mob/living/simple_animal/jacq/update_mobility()
. = ..()
if(busy)
@@ -478,7 +511,150 @@
. |= MOBILITY_MOVE
mobility_flags = .
//Christmas car spawner
/mob/living/simple_animal/jacq/car_spawner
name = "Jacqueline cars terminal"
icon_state = "jacq_cars_spawner"
AIStatus = AI_OFF
spawn_cars = TRUE
active = FALSE
/mob/living/simple_animal/jacq/car_spawner/Destroy()
visible_message("The <b>[src]</b> gives out an error sound, <span class='spooky'>\"Ey! Bugger off!\"</span>")
fully_heal(FALSE)
return ..()
/mob/living/simple_animal/jacq/car_spawner/death()
visible_message("The <b>[src]</b> gives out an error sound, <span class='spooky'>\"Ey! Bugger off!\"</span>")
fully_heal(FALSE)
/mob/living/simple_animal/jacq/car_spawner/poof()
if(!active)//if disabled, don't poof
return
var/datum/reagents/R = new/datum/reagents(100)//Hey, just in case.
var/datum/effect_system/smoke_spread/chem/s = new()
R.add_reagent(/datum/reagent/fermi/secretcatchem, 10)
s.set_up(R, 0, loc)
s.start()
stopmove()
health = 25
/mob/living/simple_animal/jacq/car_spawner/spawn_cars(mob/living/carbon/C)
visible_message("<b>[src]</b> boots up and displays jacq's glowing smile, <span class='spooky'>\"Hallo there user! Merry Christmas! What ken type o' craft ken Ah offer ye? I can magic up a vectorcraft in manual, automatic or customise it if yer feeling technical.\"</span>")
jacqrunes("Hallo there user! What ken type o' craft ken Ah offer ye? I can magic up a vectorcraft in manual, automatic or customise it if yer feeling technical.", C)
var/choices_reward = list("Manual", "Automatic", "Customise", "Are you a computer now Jacq?", "Nothing, thanks")
var/choice_reward = input(usr, "Merry Trick_or_Treat.exe initiated!", "Merry Trick_or_Treat.exe initiated!") in choices_reward
switch(choice_reward)
if("Manual")
visible_message("The <b>[src]</b> makes a magical booping sound, <span class='spooky'>\"Great choice! 'Ere's yer car.\"</span>")
jacqrunes("Great choice! 'Ere's yer car.", C)
new /obj/vehicle/sealed/vectorcraft(loc)
if("Automatic")
visible_message("<b>[src]</b> makes a magical booping sound, <span class='spooky'>\"'Ere's yer car. Not as fast as an automatic mind.\"</span>")
jacqrunes("'Ere's yer car. Not as fast as an automatic mind.", C)
new /obj/vehicle/sealed/vectorcraft/auto(loc)
if("Are you a computer now Jacq?")
visible_message("<b>[src]</b> makes a frustrated error sound, <span class='spooky'>\"Nae, are ye daft? Ah built these thingies tae magic up cars fer ye. Well, I got a speccy four eyes tae do it fer me, but me names on it like cause it was me idea.\"</span>")
jacqrunes("Nae, are ye daft? Ah built these thingies tae magic up cars fer ye. Well, I got a speccy four eyes tae do it fer me, but me names on it like cause it was me idea.", C)
if("Customise")
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Arright, Manual or Automatic?\"</span>")
var/choices_transm = list("Manual", "Automatic")
var/choice_transm = input(usr, "Choose transmission", "Choose transmission") in choices_transm
var/points = 0
var/obj/vehicle/sealed/vectorcraft/VC
switch(choice_transm)
if("Manual")
VC = new /obj/vehicle/sealed/vectorcraft(loc)
if("Automatic")
VC = new /obj/vehicle/sealed/vectorcraft/auto(loc)
points += 500
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Maximum acceleration? (default [VC.max_acceleration], max 10)\"</span>")
var/max_accl = text2num(input(usr, "Maximum acceleration? (default [VC.max_acceleration], max 10)", "[VC.max_acceleration]"))
max_accl = clamp(max_accl, 0, 10)
VC.max_acceleration = max_accl
VC.i_m_acell = max_accl
points += max_accl*10
/* This is internally used
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Acceleration step? (default 0.3, max 1)\"</span>")
var/max_accl_s = text2num(input(usr, "Acceleration step? (default 0.3, max 1)", "[VC.accel_step]"))
max_accl_s = clamp(max_accl_s, 0, 1)
VC.max_acceleration = max_accl_s
points += max_accl_s*100
*/
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Acceleration? (default [VC.acceleration], max 2)\"</span>")
var/accl = text2num(input(usr, "Acceleration? (default [VC.acceleration], max 2)", "[VC.acceleration]"))
accl = clamp(accl, 0, 2)
VC.acceleration = accl
VC.i_acell = accl
points += accl*100
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Maximum deceleration? (default [VC.max_deceleration], max 15)\"</span>")
var/deaccl = text2num(input(usr, "Max_deceleration? (default [VC.max_deceleration], max 15)", "[VC.max_deceleration]"))
deaccl = clamp(deaccl, 0, 15)
VC.max_deceleration = deaccl
VC.i_m_decell = deaccl
points += deaccl*10
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Maximum velocity? (default [VC.max_velocity], max 200)\"</span>")
var/m_velo = text2num(input(usr, "Maximum velocity? (default [VC.max_velocity], max 200)", "[VC.max_velocity]"))
m_velo = clamp(m_velo, 0, 200)
VC.max_velocity = m_velo
points += m_velo
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Boost power? (default [VC.boost_power], max 200)\"</span>")
var/boost = text2num(input(usr, "Boost power? (default [VC.boost_power], max 200)", "[VC.boost_power]"))
boost = clamp(boost, 0, 200)
VC.boost_power = boost
VC.i_boost = boost
points += boost
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Health points? (default [VC.max_integrity], max 1000)\"</span>")
var/health = text2num(input(usr, "Health points? (default [VC.max_integrity], max 1000)", "[VC.max_integrity]"))
health = clamp(health, 0, 500)
VC.max_integrity = health
points += health/2
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Arright, 'ow do ye want it tae look?\"</span>")
var/choices_icon = list("Racer", "Truck", "Cyber", "Ambulance", "Pod", "Clown")
var/choice_icon = input(usr, "Choose body", "Choose body") in choices_icon
switch(choice_icon)
if("Truck")
VC.icon_state = "truck"
if("Cyber")
VC.icon_state = "cyber"
if("Ambulance")
VC.icon_state = "ambutruck"
if("Pod")
VC.icon_state = "engineering_pod"
if("Clown")
VC.icon_state = "clowncar"
visible_message("The <b>[src]</b> pings, <span class='spooky'>\"Finally; what name are ye gonna give it?\"</span>")
var/choice_name = input(usr, "Pick a name!", "")
choice_name += " (Points cost:[points])"
VC.name = choice_name
visible_message("The <b>[src]</b> gives a final boop, <span class='spooky'>\"There ye be, enjoy!\"</span>")
if("How do Automatics work?")
visible_message("The image of Jacq on the <b>[src]</b> smiles, <span class='spooky'>\"Hold wasd to gain speed in a direction, c to enable/disable the clutch, 1 2 3 4 to change gears (help is gear 1, disarm is gear 2, grab is gear 3 and harm is gear 4) while holding a direction (make sure the clutch is enabled when you change gears, you should hear a sound when you've successfully changed gears), r to toggle handbrake, hold alt for brake and press shift for boost (the machine will beep when the boost is recharged)! If you hear an ebbing sound like \"brbrbrbrbr\" you need to gear down, the whining sound means you need to gear up. Hearing a pleasant \"whumwhumwhum\" is optimal gearage! It can be a lil slow to start, so make sure you're in the 1st gear, andusing a boost to get you started is a good idea. If you've got a good speed you'll likely never need to dip down to gear 1 again, and make sure to hold the acceleration pedal down while changing gears (hold a direction). 1st gear is for slow movement, and it's a good idea to mvoe to 2nd gear as quick as you can, you can coldstart a car from gear one by slowly moving, then using the boost to jump you up to gear 2 speeds. The upper gears are for unlimiting your top speed.\"</span>")
jacqrunes("They're a bit tricky, aye. Basically;", C)
if("Nothing, thanks")
visible_message("The image of Jacq on the <b>[src]</b> shrugs, <span class='spooky'>\"Suit yerself.\"</span>")
jacqrunes("Suit yerself.", C)
visible_message("The <b>[src]</b> beeps, <span class='spooky'>\"Oh and look after the crafts, aye? They can get a wee bit... explosive if banged up a tad too much. They move slower damaged too like. Ye can repair 'em with the welders o'er there.\"</span>")
jacqrunes("Oh and look after the crafts, aye? They can get a wee bit... explosive if banged up a tad too much. They move slower damaged too like. Ye can repair 'em with the welders o'er there. ", C)
/obj/item/clothing/head/hardhat/pumpkinhead/jaqc
name = "Jacq o' latern"
@@ -555,9 +731,13 @@
/obj/item/reagent_containers/potion_container/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
..()
delayed_release_smoke()
/obj/item/reagent_containers/potion_container/proc/delayed_release_smoke()
set waitfor = FALSE
sleep(20)
var/datum/effect_system/smoke_spread/chem/s = new()
s.set_up(src.reagents, 3, src.loc)
s.set_up(src.reagents, 3, get_turf(src))
s.start()
qdel(src)
+1 -1
View File
@@ -203,7 +203,7 @@
return
emergency_shutdown()
/obj/machinery/computer/holodeck/ex_act(severity, target)
/obj/machinery/computer/holodeck/ex_act(severity, target, origin)
emergency_shutdown()
return ..()
+2 -2
View File
@@ -27,10 +27,10 @@
QDEL_NULL(beaker)
return ..()
/obj/machinery/biogenerator/contents_explosion(severity, target)
/obj/machinery/biogenerator/contents_explosion(severity, target, origin)
..()
if(beaker)
beaker.ex_act(severity, target)
beaker.ex_act(severity, target, origin)
/obj/machinery/biogenerator/handle_atom_del(atom/A)
..()
+1 -1
View File
@@ -173,7 +173,7 @@
var/mob/M = loc
M.dropItemToGround(src)
/obj/item/reagent_containers/food/snacks/grown/firelemon/ex_act(severity)
/obj/item/reagent_containers/food/snacks/grown/firelemon/ex_act(severity, target, origin)
qdel(src) //Ensuring that it's deleted by its own explosion
/obj/item/reagent_containers/food/snacks/grown/firelemon/proc/prime(mob/living/lanced_by)
+2 -2
View File
@@ -224,7 +224,7 @@
if(!QDELETED(src))
qdel(src)
/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/ex_act(severity)
/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/ex_act(severity, target, origin)
qdel(src) //Ensuring that it's deleted by its own explosion. Also prevents mass chain reaction with piles of cherry bombs
/obj/item/reagent_containers/food/snacks/grown/cherry_bomb/proc/prime(mob/living/lanced_by)
@@ -500,7 +500,7 @@
log_game("Coconut bomb detonation at [AREACOORD(T)], location [loc]")
qdel(src)
/obj/item/reagent_containers/food/snacks/grown/coconut/ex_act(severity)
/obj/item/reagent_containers/food/snacks/grown/coconut/ex_act(severity, target, origin)
qdel(src)
/obj/item/reagent_containers/food/snacks/grown/coconut/deconstruct(disassembled = TRUE)
+15 -25
View File
@@ -53,31 +53,21 @@
/obj/item/seeds/replicapod/on_reagent_change(changetype)
if(changetype == ADD_REAGENT)
var/datum/reagent/blood/B = reagents.has_reagent(/datum/reagent/blood)
if(B)
if(B.data["mind"] && B.data["cloneable"])
mind = B.data["mind"]
ckey = B.data["ckey"]
realName = B.data["real_name"]
blood_gender = B.data["gender"]
blood_type = B.data["blood_type"]
features = B.data["features"]
factions = B.data["factions"]
quirks = B.data["quirks"]
contains_sample = TRUE
visible_message("<span class='notice'>The [src] is injected with a fresh blood sample.</span>")
else
visible_message("<span class='warning'>The [src] rejects the sample!</span>")
if(!reagents.has_reagent(/datum/reagent/blood))
mind = null
ckey = null
realName = null
blood_gender = null
blood_type = null
features = null
factions = null
contains_sample = FALSE
for(var/datum/reagent/R as anything in reagents.reagent_list)
if(R.data["mind"])
if(R.data["cloneable"])
mind = R.data["mind"]
ckey = R.data["ckey"]
realName = R.data["real_name"]
blood_gender = R.data["gender"]
blood_type = R.data["blood_type"]
features = R.data["features"]
factions = R.data["factions"]
quirks = R.data["quirks"]
contains_sample = TRUE
visible_message("<span class='notice'>The [src] is injected with a fresh blood sample.</span>")
else
visible_message("<span class='warning'>The [src] rejects the sample!</span>")
/obj/item/seeds/replicapod/get_analyzer_text()
var/text = ..()
@@ -26,7 +26,7 @@
"light blue" = COLOR_ASSEMBLY_LBLUE,
"blue" = COLOR_ASSEMBLY_BLUE,
"purple" = COLOR_ASSEMBLY_PURPLE,
"pink" = COLOR_ASSEMBLY_PINK,
"pink" = LIGHT_COLOR_PINK,
"custom" = COLOR_ASSEMBLY_WHITE
)
@@ -36,3 +36,25 @@
/datum/keybinding/living/toggle_resting/down(client/user)
var/mob/living/L = user.mob
L.lay_down()
/datum/keybinding/living/cancel_action
hotkey_keys = list("Unbound")
name = "cancel_action"
full_name = "Cancel Action"
description = "Cancel the current action."
/// Technically you shouldn't be doing any actions if you were sleeping either but...
/datum/keybinding/living/can_use(client/user)
. = ..()
var/mob/living/mob = user.mob
return . && (mob.stat == CONSCIOUS)
/datum/keybinding/living/cancel_action/down(client/user)
var/mob/M = user.mob
if(length(M.do_afters))
var/atom/target = M.do_afters[M.do_afters.len]
to_chat(M, "<span class='notice'>You stop interacting with \the [target].</span>")
LAZYREMOVE(M.do_afters, target)
else
to_chat(M, "<span class='notice'>There's nothing that you can cancel right now.</span>")
return TRUE
+1 -1
View File
@@ -5,7 +5,7 @@
speech_verb = "chitter"
ask_verb = "chitter"
exclaim_verb = "chitter"
key = "r"
key = "c"
flags = NO_STUTTER | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD
icon_state = "arachnid"
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
ask_verb = "queries"
exclaim_verb = "declares"
whisper_verb = "hushes"
key = "k"
key = "j"
space_chance = 40
default_priority = 94
flags = TONGUELESS_SPEECH
+7
View File
@@ -193,6 +193,13 @@
new /obj/item/book/manual/wiki/research_and_development(src)
update_icon()
/obj/structure/bookcase/manuals/medical
name = "medical manuals bookcase"
/obj/structure/bookcase/manuals/medical/Initialize()
. = ..()
new /obj/item/book/manual/wiki/medical_cloning(src)
update_icon()
/*
* Book
+1 -1
View File
@@ -26,7 +26,7 @@
color = GLOB.em_block_color
/atom/movable/emissive_blocker/ex_act(severity)
/atom/movable/emissive_blocker/ex_act(severity, target, origin)
return FALSE
/atom/movable/emissive_blocker/singularity_act()
+1 -1
View File
@@ -124,7 +124,7 @@
// Variety of overrides so the overlays don't get affected by weird things.
/atom/movable/lighting_object/ex_act(severity)
/atom/movable/lighting_object/ex_act(severity, target, origin)
return 0
/atom/movable/lighting_object/singularity_act()
+184
View File
@@ -0,0 +1,184 @@
//Map exporter
//Inputting a list of turfs into convert_map_to_tgm() will output a string
//with the turfs and their objects / areas on said turf into the TGM mapping format
//for .dmm files. This file can then be opened in the map editor or imported
//back into the game.
//============================
//This has been made semi-modular so you should be able to use these functions
//elsewhere in code if you ever need to get a file in the .dmm format
/atom/proc/get_save_vars()
return
GLOBAL_LIST_INIT(save_file_chars, list(
"a","b","c","d","e",
"f","g","h","i","j",
"k","l","m","n","o",
"p","q","r","s","t",
"u","v","w","x","y",
"z","A","B","C","D",
"E","F","G","H","I",
"J","K","L","M","N",
"O","P","Q","R","S",
"T","U","V","W","X",
"Y","Z"
))
//Converts a list of turfs into TGM file format
/proc/convert_map_to_tgm(list/map,\
save_flag = SAVE_ALL, \
shuttle_area_flag = SAVE_SHUTTLEAREA_DONTCARE, \
list/vars_to_save = list("pixel_x", "pixel_y", "dir", "name", "req_access", "req_access_txt", "piping_layer", "color", "icon_state", "pipe_color", "amount"),\
list/obj_blacklist = list())
//Calculate the bounds
var/minx = 1024
var/miny = 1024
var/maxx = -1
var/maxy = -1
for(var/turf/place in map)
minx = min(place.x, minx)
miny = min(place.y, miny)
maxx = max(place.x, maxx)
maxy = max(place.y, maxy)
var/width = maxx - minx + 1
var/height = maxy - miny + 1
//Sort the map so weird shaped / bad inputted maps can be handled
var/list/sortedmap = sort_map(map, minx, miny, maxx, maxy)
//Step 0: Calculate the amount of letters we need (26 ^ n > turf count)
var/turfsNeeded = width * height
var/layers = FLOOR(log(GLOB.save_file_chars.len, turfsNeeded) + 0.999,1)
//Step 1: Run through the area and generate file data
var/list/header_chars = list() //The characters of the header
var/list/header_dat = list() //The data of the header, lines up with chars
var/header = "" //The actual header in text
var/contents = "" //The contents in text (bit at the end)
var/index = 1
for(var/x in 1 to width)
contents += "\n([x],1,1) = {\"\n"
for(var/y in height to 1 step -1)
//====Get turfs Data====
var/turf/place = sortedmap[x][y]
var/area/location
var/list/objects
//If there is nothing there, save as a noop (For odd shapes)
if(!place)
place = /turf/template_noop
location = /area/template_noop
objects = list()
//Ignore things in space, must be a space turf and the area has to be empty space
else if(istype(place, /turf/open/space) && istype(get_area(place), /area/space) && !(save_flag & SAVE_SPACE))
place = /turf/template_noop
location = /area/template_noop
//Stuff to add
else
var/area/location_type = get_area(place)
location = location_type.type
objects = place
place = place.type
//====Saving shuttles only / non shuttles only====
var/is_shuttle_area = istype(location, /area/shuttle)
if((is_shuttle_area && shuttle_area_flag == SAVE_SHUTTLEAREA_IGNORE) || (!is_shuttle_area && shuttle_area_flag == SAVE_SHUTTLEAREA_ONLY))
place = /turf/template_noop
location = /area/template_noop
objects = list()
//====For toggling not saving areas and turfs====
if(!(save_flag & SAVE_AREAS))
location = /area/template_noop
if(!(save_flag & SAVE_TURFS))
place = /turf/template_noop
//====Generate Header Character====
var/header_char = calculate_tgm_header_index(index, layers) //The characters of the header
var/current_header = "(\n" //The actual stuff inside the header
//Add objects to the header file
var/empty = TRUE
//====SAVING OBJECTS====
if(save_flag & SAVE_OBJECTS)
for(var/obj/thing in objects)
if(thing.type in obj_blacklist)
continue
var/metadata = generate_tgm_metadata(thing, vars_to_save)
current_header += "[empty?"":",\n"][thing.type][metadata]"
empty = FALSE
//====SAVING SPECIAL DATA====
//This is what causes lockers and machines to save stuff inside of them
if(save_flag & SAVE_OBJECT_PROPERTIES)
var/custom_data = thing.on_object_saved()
current_header += "[custom_data ? ",\n[custom_data]" : ""]"
//====SAVING MOBS====
if(save_flag & SAVE_MOBS)
for(var/mob/living/thing in objects)
if(istype(thing, /mob/living/carbon)) //Ignore people, but not animals
continue
var/metadata = generate_tgm_metadata(thing, vars_to_save)
current_header += "[empty?"":",\n"][thing.type][metadata]"
empty = FALSE
current_header += "[empty?"":",\n"][place],\n[location])\n"
//====Fill the contents file====
//Compression is done here
var/position_of_header = header_dat.Find(current_header)
if(position_of_header)
//If the header has already been saved, change the character to the other saved header
header_char = header_chars[position_of_header]
else
header += "\"[header_char]\" = [current_header]"
header_chars += header_char
header_dat += current_header
index ++
contents += "[header_char]\n"
contents += "\"}"
return "[header][contents]"
//Sorts maps in terms of their positions, so scrambled / odd shaped maps can be saved
/proc/sort_map(list/map, minx, miny, maxx, maxy)
var/width = maxx - minx + 1
var/height = maxy - miny + 1
var/allTurfs = new/list(width, height)
for(var/turf/place in map)
allTurfs[place.x - minx + 1][place.y - miny + 1] = place
return allTurfs
//vars_to_save = list() to save all vars
/proc/generate_tgm_metadata(atom/O, list/vars_to_save = list("pixel_x", "pixel_y", "dir", "name", "req_access", "req_access_txt", "piping_layer", "color", "icon_state", "pipe_color", "amount"))
var/dat = ""
var/data_to_add = list()
for(var/V in O.vars)
if(O.get_save_vars())
if(!(V in O.get_save_vars()))
continue
else
if(!(V in vars_to_save) && vars_to_save)
continue
var/value = O.vars[V]
if(!value)
continue
if(value == initial(O.vars[V]) || !issaved(O.vars[V]))
continue
var/symbol = ""
if(istext(value))
symbol = "\""
value = sanitize_simple(value, list("{"="", "}"="", "\""="", ";"="", ","=""))
else if(isicon(value) || isfile(value))
symbol = "'"
else if(!(isnum(value) || ispath(value)))
continue
//Prevent symbols from being because otherwise you can name something [";},/obj/item/gun/energy/laser/instakill{name="da epic gun] and spawn yourself an instakill gun.
data_to_add += "[V] = [symbol][value][symbol]"
//Process data to add
var/first = TRUE
for(var/data in data_to_add)
dat += "[first ? "" : ";\n"]\t[data]"
first = FALSE
if(dat)
dat = "{\n[dat]\n\t}"
return dat
/proc/calculate_tgm_header_index(index, layers)
var/output = ""
for(var/i in 1 to layers)
var/l = GLOB.save_file_chars.len
var/c = FLOOR((index-1) / (l ** (i - 1)), 1)
c = (c % l) + 1
output = "[GLOB.save_file_chars[c]][output]"
return output

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