Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Ghommie-cit367
This commit is contained in:
@@ -327,7 +327,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
for(var/obj/machinery/light/L in src)
|
||||
L.update()
|
||||
|
||||
/area/proc/updateicon()
|
||||
/area/proc/update_icon()
|
||||
var/weather_icon
|
||||
for(var/V in SSweather.processing)
|
||||
var/datum/weather/W = V
|
||||
@@ -337,7 +337,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
if(!weather_icon)
|
||||
icon_state = null
|
||||
|
||||
/area/space/updateicon()
|
||||
/area/space/update_icon()
|
||||
icon_state = null
|
||||
|
||||
/*
|
||||
@@ -370,7 +370,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
|
||||
/area/proc/power_change()
|
||||
for(var/obj/machinery/M in src) // for each machine in the area
|
||||
M.power_change() // reverify power status (to update icons etc.)
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
/area/proc/usage(chan)
|
||||
var/used = 0
|
||||
|
||||
+2
-6
@@ -316,9 +316,6 @@
|
||||
to_chat(user, "<span class='warning'>You can't move while buckled to [src]!</span>")
|
||||
return
|
||||
|
||||
/atom/proc/prevent_content_explosion()
|
||||
return FALSE
|
||||
|
||||
/atom/proc/contents_explosion(severity, target)
|
||||
return //For handling the effects of explosions on contents that would not normally be effected
|
||||
|
||||
@@ -812,8 +809,7 @@ Proc for attack log creation, because really why not
|
||||
|
||||
// Filter stuff
|
||||
/atom/movable/proc/add_filter(name,priority,list/params)
|
||||
if(!filter_data)
|
||||
filter_data = list()
|
||||
LAZYINITLIST(filter_data)
|
||||
var/list/p = params.Copy()
|
||||
p["priority"] = priority
|
||||
filter_data[name] = p
|
||||
@@ -821,7 +817,7 @@ Proc for attack log creation, because really why not
|
||||
|
||||
/atom/movable/proc/update_filters()
|
||||
filters = null
|
||||
sortTim(filter_data,associative = TRUE)
|
||||
filter_data = sortTim(filter_data, /proc/cmp_filter_data_priority, TRUE)
|
||||
for(var/f in filter_data)
|
||||
var/list/data = filter_data[f]
|
||||
var/list/arguments = data.Copy()
|
||||
|
||||
@@ -189,7 +189,13 @@
|
||||
if(tod)
|
||||
var/tdelta = round(world.time - timeofdeath)
|
||||
if(tdelta < (DEFIB_TIME_LIMIT * 10))
|
||||
holder.icon_state = "huddefib"
|
||||
var/obj/item/organ/heart/He = getorgan(/obj/item/organ/heart)
|
||||
if(He)
|
||||
holder.icon_state = "huddefib"
|
||||
if(He.organ_flags & ORGAN_FAILING)
|
||||
holder.icon_state = "huddefibheart"
|
||||
else
|
||||
holder.icon_state = "huddefibheart"
|
||||
return
|
||||
holder.icon_state = "huddead"
|
||||
else
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
|
||||
/datum/game_mode
|
||||
var/list/datum/mind/bloodsuckers = list() // List of minds belonging to this game mode.
|
||||
var/list/datum/mind/vassals = list() // List of minds that have been turned into Vassals.
|
||||
//var/list/datum/mind/vamphunters = list() // List of minds hunting vampires. Disabled at the moment
|
||||
var/obj/effect/sunlight/bloodsucker_sunlight // Sunlight Timer. Created on first Bloodsucker assign. Destroyed on last removed Bloodsucker.
|
||||
|
||||
// LISTS //
|
||||
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
|
||||
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
|
||||
// The antags you're allowed to be if turning Vassal.
|
||||
|
||||
/datum/game_mode/bloodsucker
|
||||
name = "bloodsucker"
|
||||
config_tag = "bloodsucker"
|
||||
traitor_name = "Bloodsucker"
|
||||
antag_flag = ROLE_BLOODSUCKER
|
||||
false_report_weight = 1
|
||||
restricted_jobs = list("AI","Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_players = 20
|
||||
required_enemies = 2
|
||||
recommended_enemies = 4
|
||||
reroll_friendly = FALSE
|
||||
enemy_minimum_age = 7
|
||||
round_ends_with_antag_death = FALSE
|
||||
|
||||
|
||||
announce_span = "danger"
|
||||
announce_text = "Filthy, bloodsucking vampires are crawling around disguised as crewmembers!\n\
|
||||
<span class='danger'>Bloodsuckers</span>: The crew are cattle, while you are both shepherd and slaughterhouse.\n\
|
||||
<span class='notice'>Crew</span>: Put an end to the undead infestation before the station is overcome!"
|
||||
|
||||
/datum/game_mode/bloodsucker/generate_report()
|
||||
return "Reports indicate that some of your crew may have toppled statues in the past week, angering the gods and becoming cursed with undeath and a desire for blood. Watch out for crewmembers that seem to shun the light or are found pale and delirious."
|
||||
|
||||
// Seems to be run by game ONCE, and finds all potential players to be antag.
|
||||
/datum/game_mode/bloodsucker/pre_setup()
|
||||
|
||||
// Set Restricted Jobs
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
// Set number of Vamps
|
||||
recommended_enemies = CLAMP(round(num_players()/10), 1, 6);
|
||||
|
||||
// Select Antags
|
||||
for(var/i = 0, i < recommended_enemies, i++)
|
||||
if (!antag_candidates.len)
|
||||
break
|
||||
var/datum/mind/bloodsucker = pick(antag_candidates)
|
||||
// Can we even BE a bloodsucker?
|
||||
//if (can_make_bloodsucker(bloodsucker, display_warning=FALSE))
|
||||
bloodsuckers += bloodsucker
|
||||
bloodsucker.restricted_roles = restricted_jobs
|
||||
log_game("[bloodsucker.key] (ckey) has been selected as a Bloodsucker.")
|
||||
antag_candidates.Remove(bloodsucker) // Apparently you can also write antag_candidates -= bloodsucker
|
||||
|
||||
// Assign Hunters (as many as monsters, plus one)
|
||||
//assign_monster_hunters(bloodsuckers.len, TRUE, bloodsuckers) // Disabled for now
|
||||
|
||||
// Do we have enough vamps to continue?
|
||||
return bloodsuckers.len >= required_enemies
|
||||
|
||||
|
||||
// Gamemode is all done being set up. We have all our Vamps. We now pick objectives and let them know what's happening.
|
||||
/datum/game_mode/bloodsucker/post_setup()
|
||||
|
||||
// Sunlight (Creating Bloodsuckers manually will check to create this, too)
|
||||
check_start_sunlight()
|
||||
|
||||
// Vamps
|
||||
for(var/datum/mind/bloodsucker in bloodsuckers)
|
||||
// spawn() --> Run block of code but game continues on past it.
|
||||
// sleep() --> Run block of code and freeze code there (including whoever called us) until it's resolved.
|
||||
|
||||
//Clean Bloodsucker Species (racist?)
|
||||
//clean_invalid_species(bloodsucker)
|
||||
// TO-DO !!!
|
||||
|
||||
// Add Bloodsucker Antag Datum (or remove from list on Fail)
|
||||
if (!make_bloodsucker(bloodsucker))
|
||||
bloodsuckers -= bloodsucker
|
||||
|
||||
// NOTE: Hunters are done in ..() parent proc
|
||||
|
||||
return ..()
|
||||
|
||||
// Checking for ACTUALLY Dead Vamps
|
||||
/datum/game_mode/bloodsucker/are_special_antags_dead()
|
||||
// Bloodsucker not Final Dead
|
||||
for(var/datum/mind/bloodsucker in bloodsuckers)
|
||||
if(!bloodsucker.AmFinalDeath())
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
|
||||
/datum/game_mode/proc/check_start_sunlight()
|
||||
// Already Sunlight (and not about to cancel)
|
||||
if (istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
|
||||
return
|
||||
bloodsucker_sunlight = new ()
|
||||
|
||||
// End Sun (last bloodsucker removed)
|
||||
/datum/game_mode/proc/check_cancel_sunlight()
|
||||
// No Sunlight
|
||||
if (!istype(bloodsucker_sunlight))
|
||||
return
|
||||
if (bloodsuckers.len <= 0)
|
||||
bloodsucker_sunlight.cancel_me = TRUE
|
||||
qdel(bloodsucker_sunlight)
|
||||
bloodsucker_sunlight = null
|
||||
|
||||
/datum/game_mode/proc/is_daylight()
|
||||
return istype(bloodsucker_sunlight) && bloodsucker_sunlight.amDay
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/datum/game_mode/proc/can_make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator, display_warning=TRUE) // Creator is just here so we can display fail messages to whoever is turning us.
|
||||
// No Mind
|
||||
if(!bloodsucker || !bloodsucker.key) // KEY is client login?
|
||||
//if(creator) // REMOVED. You wouldn't see their name if there is no mind, so why say anything?
|
||||
// to_chat(creator, "<span class='danger'>[bloodsucker] isn't self-aware enough to be raised as a Bloodsucker!</span>")
|
||||
return FALSE
|
||||
// Current body is invalid
|
||||
if(!ishuman(bloodsucker.current))// && !ismonkey(bloodsucker.current))
|
||||
if(display_warning && creator)
|
||||
to_chat(creator, "<span class='danger'>[bloodsucker] isn't evolved enough to be raised as a Bloodsucker!</span>")
|
||||
return FALSE
|
||||
// Species Must have a HEART (Sorry Plasmabois)
|
||||
var/mob/living/carbon/human/H = bloodsucker.current
|
||||
if(NOBLOOD in H.dna.species.species_traits)
|
||||
if(display_warning && creator)
|
||||
to_chat(creator, "<span class='danger'>[bloodsucker]'s DNA isn't compatible!</span>")
|
||||
return FALSE
|
||||
// Already a Non-Human Antag
|
||||
if(bloodsucker.has_antag_datum(/datum/antagonist/abductor) || bloodsucker.has_antag_datum(/datum/antagonist/devil) || bloodsucker.has_antag_datum(/datum/antagonist/changeling))
|
||||
return FALSE
|
||||
// Already a vamp
|
||||
if(bloodsucker.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
|
||||
if(display_warning && creator)
|
||||
to_chat(creator, "<span class='danger'>[bloodsucker] is already a Bloodsucker!</span>")
|
||||
return FALSE
|
||||
// Not High Enough
|
||||
if(creator)
|
||||
var/datum/antagonist/bloodsucker/creator_bloodsucker = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
if(!istype(creator_bloodsucker) || creator_bloodsucker.vamplevel < BLOODSUCKER_LEVEL_TO_EMBRACE)
|
||||
to_chat(creator, "<span class='danger'>Your blood is too thin to turn this corpse!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/game_mode/proc/make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator = null) // NOTE: This is a game_mode/proc, NOT a game_mode/bloodsucker/proc! We need to access this function despite the game mode.
|
||||
if (!can_make_bloodsucker(bloodsucker))
|
||||
return FALSE
|
||||
|
||||
// Create Datum: Fledgling
|
||||
var/datum/antagonist/bloodsucker/A
|
||||
|
||||
// [FLEDGLING]
|
||||
if (creator)
|
||||
A = new (bloodsucker)
|
||||
A.creator = creator
|
||||
bloodsucker.add_antag_datum(A)
|
||||
// Log
|
||||
message_admins("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
|
||||
log_admin("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
|
||||
|
||||
// [MASTER]
|
||||
else
|
||||
A = bloodsucker.add_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/game_mode/proc/remove_bloodsucker(datum/mind/bloodsucker)
|
||||
bloodsucker.remove_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
|
||||
|
||||
/datum/game_mode/proc/clean_invalid_species(datum/mind/bloodsucker)
|
||||
// Only checking for Humans here
|
||||
if (!ishuman(bloodsucker.current) || !bloodsucker.current.client)
|
||||
return
|
||||
var/am_valid = TRUE
|
||||
var/mob/living/carbon/human/H = bloodsucker.current
|
||||
|
||||
// Check if PLASMAMAN?
|
||||
if(NOBLOOD in H.dna.species.species_traits)
|
||||
am_valid = FALSE
|
||||
|
||||
// PROBLEM:
|
||||
//
|
||||
// Setting species leaves clothes on. If you were a plasmaman, we need to reassign your entire outfit. Otherwise
|
||||
// everyone will wonder why you're a human with Plasma clothes (jk they'll know you're antag)
|
||||
|
||||
// Convert to HUMAN (along with ID and PDA)
|
||||
if (!am_valid)
|
||||
H.set_species(/datum/species/human)
|
||||
H.real_name = H.client.prefs.custom_names["human"]
|
||||
var/obj/item/card/id/ID = H.wear_id?.GetID()
|
||||
if(ID)
|
||||
ID.registered_name = H.real_name
|
||||
ID.update_label()
|
||||
|
||||
|
||||
/datum/game_mode/proc/can_make_vassal(mob/living/target, datum/mind/creator, display_warning=TRUE)//, check_antag_or_loyal=FALSE)
|
||||
// Not Correct Type: Abort
|
||||
if (!iscarbon(target) || !creator)
|
||||
return FALSE
|
||||
if (target.stat > UNCONSCIOUS)
|
||||
return FALSE
|
||||
// Check Overdose: Am I even addicted to blood? Do I even have any in me?
|
||||
//if (!target.reagents.addiction_list || !target.reagents.reagent_list)
|
||||
//message_admins("DEBUG2: can_make_vassal() Abort: No reagents")
|
||||
// return 0
|
||||
// Check Overdose: Did my current volume go over the Overdose threshold?
|
||||
//var/am_addicted = 0
|
||||
//for (var/datum/reagent/blood/vampblood/blood in target.reagents.addiction_list) // overdosed is tracked in reagent_list, not addiction_list.
|
||||
//message_admins("DEBUG3: can_make_vassal() Found Blood! [blood] [blood.overdose]")
|
||||
//if (blood.overdosed)
|
||||
// am_addicted = 1 // Blood is present in addiction? That's all we need.
|
||||
// break
|
||||
|
||||
//if (!am_addicted)
|
||||
//message_admins("DEBUG4: can_make_vassal() Abort: No Blood")
|
||||
// return 0
|
||||
// No Mind!
|
||||
if (!target.mind || !target.mind.key)
|
||||
if (display_warning)
|
||||
to_chat(creator, "<span class='danger'>[target] isn't self-aware enough to be made into a Vassal.</span>")
|
||||
return FALSE
|
||||
// Already MY Vassal
|
||||
var/datum/antagonist/vassal/V = target.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
|
||||
if (istype(V) && V.master)
|
||||
if (V.master.owner == creator)
|
||||
if (display_warning)
|
||||
to_chat(creator, "<span class='danger'>[target] is already your loyal Vassal!</span>")
|
||||
else
|
||||
if (display_warning)
|
||||
to_chat(creator, "<span class='danger'>[target] is the loyal Vassal of another Bloodsucker!</span>")
|
||||
return FALSE
|
||||
// Already Antag or Loyal (Vamp Hunters count as antags)
|
||||
if (target.mind.enslaved_to || AmInvalidAntag(target.mind)) //!VassalCheckAntagValid(target.mind, check_antag_or_loyal)) // HAS_TRAIT(target, TRAIT_MINDSHIELD, "implant") ||
|
||||
if (display_warning)
|
||||
to_chat(creator, "<span class='danger'>[target] resists the power of your blood to dominate their mind!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/game_mode/proc/AmValidAntag(datum/mind/M)
|
||||
// No List?
|
||||
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
|
||||
return FALSE
|
||||
// Am I NOT an invalid Antag? NOTE: We already excluded non-antags above. Don't worry about the "No List?" check in AmInvalidIntag()
|
||||
return !AmInvalidAntag(M)
|
||||
|
||||
/datum/game_mode/proc/AmInvalidAntag(datum/mind/M)
|
||||
// No List?
|
||||
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
|
||||
return FALSE
|
||||
// Does even ONE antag appear in this mind that isn't in the list? Then FAIL!
|
||||
for(var/datum/antagonist/antag_datum in M.antag_datums)
|
||||
if (!(antag_datum.type in vassal_allowed_antags)) // vassal_allowed_antags is a list stored in the game mode, above.
|
||||
//message_admins("DEBUG VASSAL: Found Invalid: [antag_datum] // [antag_datum.type]")
|
||||
return TRUE
|
||||
//message_admins("DEBUG VASSAL: Valid Antags! (total of [M.antag_datums.len])")
|
||||
// WHEN YOU DELETE THE ABOVE: Remove the 3 second timer on converting the vassal too.
|
||||
return FALSE
|
||||
|
||||
/datum/game_mode/proc/make_vassal(mob/living/target, datum/mind/creator)
|
||||
if (!can_make_vassal(target,creator))
|
||||
return FALSE
|
||||
// Make Vassal
|
||||
var/datum/antagonist/vassal/V = new (target.mind)
|
||||
var/datum/antagonist/bloodsucker/B = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
|
||||
V.master = B
|
||||
target.mind.add_antag_datum(V, V.master.get_team())
|
||||
// Update Bloodsucker Title (we're a daddy now)
|
||||
B.SelectTitle(am_fledgling = FALSE) // Only works if you have no title yet.
|
||||
// Log
|
||||
message_admins("[target] has become a Vassal, and is enslaved to [creator].")
|
||||
log_admin("[target] has become a Vassal, and is enslaved to [creator].")
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/proc/remove_vassal(datum/mind/vassal)
|
||||
vassal.remove_antag_datum(ANTAG_DATUM_VASSAL)
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
|
||||
/*
|
||||
// Called from game mode pre_setup()
|
||||
/datum/game_mode/proc/assign_monster_hunters(monster_count = 4, guaranteed_hunters = FALSE, list/datum/mind/exclude_from_hunter)
|
||||
|
||||
// Not all game modes GUARANTEE a hunter
|
||||
if (rand(0,2) == 0) // 50% of the time, we get fewer or NO Hunters
|
||||
if (!guaranteed_hunters)
|
||||
return
|
||||
else
|
||||
monster_count /= 2
|
||||
|
||||
var/list/no_hunter_jobs = list("AI","Cyborg")
|
||||
|
||||
// Set Restricted Jobs
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
no_hunter_jobs += list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
no_hunter_jobs += "Assistant"
|
||||
|
||||
// Find Valid Hunters
|
||||
var/list/datum/mind/hunter_candidates = get_players_for_role(ROLE_MONSTERHUNTER)
|
||||
|
||||
// Assign Hunters (as many as vamps, plus one)
|
||||
for(var/i = 1, i < monster_count, i++) // Start at 1 so we skip Hunters if there's only one sucker.
|
||||
if (!hunter_candidates.len)
|
||||
break
|
||||
// Assign Hunter
|
||||
var/datum/mind/hunter = pick(hunter_candidates)
|
||||
hunter_candidates.Remove(hunter) // Remove Either Way
|
||||
// Already Antag? Skip
|
||||
if (islist(exclude_from_hunter) && (locate(hunter) in exclude_from_hunter)) //if (islist(hunter.antag_datums) && hunter.antag_datums.len)
|
||||
i --
|
||||
continue
|
||||
// NOTE:
|
||||
vamphunters += hunter
|
||||
hunter.restricted_roles = no_hunter_jobs
|
||||
log_game("[hunter.key] (ckey) has been selected as a Hunter.")
|
||||
|
||||
// Called from game mode post_setup()
|
||||
/datum/game_mode/proc/finalize_monster_hunters(monster_count = 4)
|
||||
var/amEvil = TRUE // First hunter is always an evil boi
|
||||
for(var/datum/mind/hunter in vamphunters)
|
||||
var/datum/antagonist/vamphunter/A = new (hunter)
|
||||
A.bad_dude = amEvil
|
||||
hunter.add_antag_datum(A)
|
||||
amEvil = FALSE // Every other hunter is just a boring greytider
|
||||
*/
|
||||
@@ -265,6 +265,7 @@
|
||||
armor = list("melee" = 40, "bullet" = 40, "laser" = 50, "energy" = 35, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
|
||||
max_temperature = 35000
|
||||
operation_req_access = list(ACCESS_SYNDICATE)
|
||||
internals_req_access = list(ACCESS_SYNDICATE)
|
||||
wreckage = /obj/structure/mecha_wreckage/honker/dark
|
||||
max_equip = 3
|
||||
spawn_tracked = FALSE
|
||||
|
||||
@@ -236,6 +236,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
return rule.round_result()
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/dynamic/generate_report()
|
||||
return "Mysterious signals that demonstrate strange dynamics have been detected in your sector. Watch out for oddities."
|
||||
|
||||
/datum/game_mode/dynamic/send_intercept()
|
||||
. = "<b><i>Central Command Status Summary</i></b><hr>"
|
||||
switch(round(threat_level))
|
||||
@@ -297,6 +300,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
/datum/game_mode/dynamic/proc/log_threat(var/log_str,var/verbose = FALSE)
|
||||
threat_log_verbose += ("[worldtime2text()]: "+log_str)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat_log",1,log_str)
|
||||
if(!verbose)
|
||||
threat_log += log_str
|
||||
|
||||
@@ -326,6 +330,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
|
||||
|
||||
threat = threat_level
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful")
|
||||
|
||||
/datum/game_mode/dynamic/can_start()
|
||||
message_admins("Dynamic mode parameters for the round:")
|
||||
@@ -337,6 +345,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if(GLOB.dynamic_forced_threat_level >= 0)
|
||||
threat_level = round(GLOB.dynamic_forced_threat_level, 0.1)
|
||||
threat = threat_level
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)")
|
||||
else
|
||||
generate_threat()
|
||||
|
||||
@@ -381,7 +390,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (roundstart_rules.len <= 0)
|
||||
log_game("DYNAMIC: [roundstart_rules.len] rules.")
|
||||
return TRUE
|
||||
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered")
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up")
|
||||
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
|
||||
rigged_roundstart()
|
||||
else
|
||||
@@ -510,7 +520,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
var/added_threat = starting_rule.scale_up(extra_rulesets_amount, threat)
|
||||
if (starting_rule.pre_execute())
|
||||
spend_threat(starting_rule.cost + added_threat)
|
||||
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat")
|
||||
log_threat("[starting_rule.ruletype] - <b>[starting_rule.name]</b> -[starting_rule.cost + starting_rule.scaled_times * starting_rule.scaling_cost] threat", verbose = TRUE)
|
||||
if(starting_rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(starting_rule.flags & ONLY_RULESET)
|
||||
@@ -534,6 +544,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if(rule.execute())
|
||||
if(rule.persistent)
|
||||
current_rules += rule
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
|
||||
return TRUE
|
||||
rule.clean_up() // Refund threat, delete teams and so on.
|
||||
executed_rules -= rule
|
||||
@@ -605,13 +616,14 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
new_rule.trim_candidates()
|
||||
if (new_rule.ready(forced))
|
||||
spend_threat(new_rule.cost)
|
||||
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> -[new_rule.cost] threat")
|
||||
log_threat("[new_rule.ruletype] - <b>[new_rule.name]</b> -[new_rule.cost] threat", verbose = TRUE)
|
||||
if (new_rule.execute()) // This should never fail since ready() returned 1
|
||||
if(new_rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(new_rule.flags & ONLY_RULESET)
|
||||
only_ruleset_executed = TRUE
|
||||
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
|
||||
executed_rules += new_rule
|
||||
if (new_rule.persistent)
|
||||
current_rules += new_rule
|
||||
@@ -626,7 +638,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (rule.execute())
|
||||
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
spend_threat(rule.cost)
|
||||
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]")
|
||||
log_threat("[rule.ruletype] [rule.name] spent [rule.cost]", verbose = TRUE)
|
||||
if(rule.flags & HIGHLANDER_RULESET)
|
||||
highlander_executed = TRUE
|
||||
else if(rule.flags & ONLY_RULESET)
|
||||
@@ -636,6 +648,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
message_admins("[key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
|
||||
log_game("DYNAMIC: [key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
|
||||
executed_rules += rule
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
|
||||
rule.candidates.Cut()
|
||||
if (rule.persistent)
|
||||
current_rules += rule
|
||||
@@ -652,6 +665,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
for (var/datum/dynamic_ruleset/rule in current_rules)
|
||||
if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing.
|
||||
current_rules -= rule
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
|
||||
|
||||
if (midround_injection_cooldown < world.time)
|
||||
if (GLOB.dynamic_forced_extended)
|
||||
@@ -670,6 +685,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
update_playercounts()
|
||||
if (get_injection_chance())
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections")
|
||||
var/cur_threat_frac = threat/threat_level
|
||||
var/list/drafted_rules = list()
|
||||
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
|
||||
@@ -685,12 +701,16 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac))
|
||||
else
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
if (drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
else
|
||||
midround_injection_cooldown = (midround_injection_cooldown + world.time)/2
|
||||
|
||||
if(event_injection_cooldown < world.time)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
message_admins("DYNAMIC: Doing event injection.")
|
||||
@@ -701,7 +721,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
if(rule.ready())
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
if(drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
|
||||
/// Updates current_players.
|
||||
@@ -792,6 +815,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
forced_latejoin_rule = null
|
||||
|
||||
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections")
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
|
||||
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
@@ -809,22 +833,29 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
|
||||
if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
|
||||
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time
|
||||
|
||||
/// Refund threat, but no more than threat_level.
|
||||
/datum/game_mode/dynamic/proc/refund_threat(regain)
|
||||
threat = min(threat_level,threat+regain)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
|
||||
log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
|
||||
|
||||
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
|
||||
/datum/game_mode/dynamic/proc/create_threat(gain)
|
||||
threat = min(100, threat+gain)
|
||||
if(threat > threat_level)
|
||||
threat_level = threat
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
|
||||
log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
|
||||
|
||||
/// Expend threat, can't fall under 0.
|
||||
/datum/game_mode/dynamic/proc/spend_threat(cost)
|
||||
threat = max(threat-cost,0)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
|
||||
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
|
||||
|
||||
/// Turns the value generated by lorentz distribution to threat value between 0 and 100.
|
||||
/datum/game_mode/dynamic/proc/lorentz_to_threat(x)
|
||||
|
||||
@@ -80,6 +80,9 @@
|
||||
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
|
||||
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
|
||||
var/delay = 0
|
||||
/// Whether or not recent-round weight values are taken into account for this ruleset.
|
||||
/// Weight reduction uses the same values as secret's recent-round mode weight reduction.
|
||||
var/always_max_weight = FALSE
|
||||
|
||||
/datum/dynamic_ruleset/New()
|
||||
..()
|
||||
@@ -91,8 +94,15 @@
|
||||
var/costs = CONFIG_GET(keyed_list/dynamic_cost)
|
||||
var/requirementses = CONFIG_GET(keyed_list/dynamic_requirements) // can't damn well use requirements
|
||||
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
|
||||
var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust)
|
||||
if(config_tag in weights)
|
||||
weight = weights[config_tag]
|
||||
var/weight_mult = 1
|
||||
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules
|
||||
for(var/i in 1 to 3)
|
||||
if(config_tag in saved_dynamic_rules[i])
|
||||
weight_mult -= (repeated_mode_adjust[i]/100)
|
||||
weight = weights[config_tag] * weight_mult
|
||||
if(config_tag in costs)
|
||||
cost = costs[config_tag]
|
||||
if(config_tag in requirementses)
|
||||
@@ -115,12 +125,15 @@
|
||||
/// If your rule has extra checks, such as counting security officers, do that in ready() instead
|
||||
/datum/dynamic_ruleset/proc/acceptable(population = 0, threat_level = 0)
|
||||
if(minimum_players > population)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to low pop")
|
||||
return FALSE
|
||||
if(maximum_players > 0 && population > maximum_players)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to high pop")
|
||||
return FALSE
|
||||
if (population >= GLOB.dynamic_high_pop_limit)
|
||||
indice_pop = 10
|
||||
if(threat_level < high_population_requirement)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level")
|
||||
log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[high_population_requirement]")
|
||||
return FALSE
|
||||
else
|
||||
@@ -132,6 +145,7 @@
|
||||
log_game("DYNAMIC: requirements and antag_cap lists have different lengths in ruleset [name]. Likely config issue, report this.")
|
||||
indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
|
||||
if(threat_level < requirements[indice_pop])
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level")
|
||||
log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[requirements[indice_pop]]")
|
||||
return FALSE
|
||||
else
|
||||
@@ -178,6 +192,7 @@
|
||||
/// IMPORTANT: If ready() returns TRUE, that means pre_execute() or execute() should never fail!
|
||||
/datum/dynamic_ruleset/proc/ready(forced = 0)
|
||||
if (required_candidates > candidates.len)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough candidates")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -212,6 +227,24 @@
|
||||
/datum/dynamic_ruleset/proc/check_finished()
|
||||
return FALSE
|
||||
|
||||
/// Returns a list to be displayed on statbus.
|
||||
/datum/dynamic_ruleset/proc/get_blackbox_info()
|
||||
var/list/ruleset_data = list()
|
||||
ruleset_data["name"] = name
|
||||
ruleset_data["rule_type"] = ruletype
|
||||
ruleset_data["cost"] = total_cost
|
||||
ruleset_data["weight"] = weight
|
||||
ruleset_data["scaled_times"] = scaled_times
|
||||
ruleset_data["antagonist_type"] = antag_datum
|
||||
ruleset_data["population_tier"] = indice_pop
|
||||
ruleset_data["assigned"] = list()
|
||||
for (var/datum/mind/M in assigned)
|
||||
var/assigned_data = list()
|
||||
assigned_data["key"] = M.key
|
||||
assigned_data["name"] = M.name
|
||||
ruleset_data["assigned"] += list(assigned_data)
|
||||
return ruleset_data
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// ROUNDSTART RULESETS //
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
var/typepath // typepath of the event
|
||||
var/triggering
|
||||
|
||||
/datum/dynamic_ruleset/event/get_blackbox_info()
|
||||
var/list/ruleset_data = list()
|
||||
ruleset_data["name"] = name
|
||||
ruleset_data["rule_type"] = ruletype
|
||||
ruleset_data["cost"] = total_cost
|
||||
ruleset_data["weight"] = weight
|
||||
ruleset_data["scaled_times"] = scaled_times
|
||||
ruleset_data["event_type"] = typepath
|
||||
ruleset_data["population_tier"] = indice_pop
|
||||
return ruleset_data
|
||||
|
||||
/datum/dynamic_ruleset/event/execute()
|
||||
var/datum/round_event/E = new typepath()
|
||||
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
|
||||
@@ -26,6 +37,7 @@
|
||||
|
||||
var/threat = round(mode.threat_level/10)
|
||||
if (job_check < required_enemies[threat])
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -125,6 +137,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
always_max_weight = TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
@@ -146,12 +159,15 @@
|
||||
|
||||
/datum/dynamic_ruleset/event/meteor_wave/ready()
|
||||
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
|
||||
name = "Meteor Wave: Threatening"
|
||||
cost = 25
|
||||
typepath = /datum/round_event/meteor_wave/threatening
|
||||
else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30))
|
||||
name = "Meteor Wave: Catastrophic"
|
||||
cost = 40
|
||||
typepath = /datum/round_event/meteor_wave/catastrophic
|
||||
else
|
||||
name = "Meteor Wave: Normal"
|
||||
cost = 15
|
||||
typepath = /datum/round_event/meteor_wave
|
||||
return ..()
|
||||
@@ -280,6 +296,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/space_dust
|
||||
name = "Minor Space Dust"
|
||||
@@ -293,6 +310,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/major_dust
|
||||
name = "Major Space Dust"
|
||||
@@ -332,6 +350,7 @@
|
||||
requirements = list(101,101,101,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/radiation_storm
|
||||
name = "Radiation Storm"
|
||||
|
||||
@@ -36,9 +36,9 @@
|
||||
continue // Dead players cannot count as opponents
|
||||
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
|
||||
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
|
||||
|
||||
var/threat = round(mode.threat_level/10)
|
||||
if (job_check < required_enemies[threat])
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
high_population_requirement = 15
|
||||
repeatable = TRUE
|
||||
flags = TRAITOR_RULESET
|
||||
always_max_weight = TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
|
||||
var/threat = round(mode.threat_level/10)
|
||||
if (job_check < required_enemies[threat])
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
@@ -94,11 +95,8 @@
|
||||
var/list/possible_candidates = list()
|
||||
possible_candidates.Add(dead_players)
|
||||
possible_candidates.Add(list_observers)
|
||||
send_applications(possible_candidates)
|
||||
if(assigned.len > 0)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
var/application_successful = send_applications(possible_candidates)
|
||||
return assigned.len > 0 && application_successful
|
||||
|
||||
/// This sends a poll to ghosts if they want to be a ghost spawn from a ruleset.
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/proc/send_applications(list/possible_volunteers = list())
|
||||
@@ -113,25 +111,18 @@
|
||||
if(!candidates || candidates.len <= required_candidates)
|
||||
message_admins("The ruleset [name] did not receive enough applications.")
|
||||
log_game("DYNAMIC: The ruleset [name] did not receive enough applications.")
|
||||
mode.refund_threat(cost)
|
||||
mode.log_threat("Rule [name] refunded [cost] (not receive enough applications)",verbose=TRUE)
|
||||
mode.executed_rules -= src
|
||||
return
|
||||
return FALSE
|
||||
|
||||
message_admins("[candidates.len] players volunteered for the ruleset [name].")
|
||||
log_game("DYNAMIC: [candidates.len] players volunteered for [name].")
|
||||
review_applications()
|
||||
return TRUE
|
||||
|
||||
/// Here is where you can check if your ghost applicants are valid for the ruleset.
|
||||
/// Called by send_applications().
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/proc/review_applications()
|
||||
for (var/i = 1, i <= required_candidates, i++)
|
||||
if(candidates.len <= 0)
|
||||
if(i == 1)
|
||||
// We have found no candidates so far and we are out of applicants.
|
||||
mode.refund_threat(cost)
|
||||
mode.log_threat("Rule [name] refunded [cost] (all applications invalid)",verbose=TRUE)
|
||||
mode.executed_rules -= src
|
||||
break
|
||||
var/mob/applicant = pick(candidates)
|
||||
candidates -= applicant
|
||||
@@ -189,6 +180,7 @@
|
||||
repeatable = TRUE
|
||||
high_population_requirement = 15
|
||||
flags = TRAITOR_RULESET
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
|
||||
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
|
||||
@@ -301,6 +293,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
if(GLOB.wizardstart.len == 0)
|
||||
log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
|
||||
@@ -363,6 +356,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -399,6 +393,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -430,6 +425,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -529,6 +525,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
@@ -567,6 +564,7 @@
|
||||
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
|
||||
return FALSE
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
|
||||
var/turf/T = get_turf(L)
|
||||
@@ -614,6 +612,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/slaughter_demon/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
@@ -665,6 +664,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/abductors/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
team = new /datum/team/abductor_team
|
||||
if(team.team_number > ABDUCTOR_MAX_TEAMS)
|
||||
@@ -704,6 +704,7 @@
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/ninja/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
if(!spawn_loc)
|
||||
var/list/spawn_locs = list()
|
||||
@@ -744,3 +745,31 @@
|
||||
|
||||
#undef ABDUCTOR_MAX_TEAMS
|
||||
#undef REVENANT_SPAWN_THRESHOLD
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker
|
||||
name = "Bloodsucker Infiltrator"
|
||||
config_tag = "latejoin_bloodsucker"
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
antag_flag = ROLE_TRAITOR
|
||||
restricted_roles = list("AI", "Cyborg")
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 3
|
||||
cost = 10
|
||||
requirements = list(90,80,70,60,55,50,45,40,35,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
assigned += M.mind
|
||||
M.mind.special_role = antag_flag
|
||||
if(mode.make_bloodsucker(M.mind))
|
||||
mode.bloodsuckers += M
|
||||
return TRUE
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
requirements = list(50,50,50,50,50,50,50,50,50,50)
|
||||
high_population_requirement = 40
|
||||
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
|
||||
always_max_weight = TRUE
|
||||
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/traitor/pre_execute()
|
||||
@@ -778,3 +779,42 @@
|
||||
var/ramp_up_final = CLAMP(round(meteorminutes/rampupdelta), 1, 10)
|
||||
|
||||
spawn_meteors(ramp_up_final, wavetype)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/bloodsucker
|
||||
name = "Bloodsuckers"
|
||||
config_tag = "bloodsucker"
|
||||
persistent = TRUE
|
||||
antag_flag = ROLE_BLOODSUCKER
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
minimum_required_age = 0
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
restricted_roles = list("Cyborg", "AI")
|
||||
required_candidates = 1
|
||||
weight = 2
|
||||
cost = 15
|
||||
scaling_cost = 10
|
||||
requirements = list(90,80,70,60,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,2)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/bloodsucker/pre_execute()
|
||||
var/num_bloodsuckers = antag_cap[indice_pop] * (scaled_times + 1)
|
||||
for (var/i = 1 to num_bloodsuckers)
|
||||
var/mob/M = pick_n_take(candidates)
|
||||
assigned += M.mind
|
||||
M.mind.special_role = ROLE_BLOODSUCKER
|
||||
M.mind.restricted_roles = restricted_roles
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/bloodsucker/execute()
|
||||
mode.check_start_sunlight()
|
||||
for(var/datum/mind/M in assigned)
|
||||
if(mode.make_bloodsucker(M))
|
||||
mode.bloodsuckers += M
|
||||
return TRUE
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
|
||||
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
|
||||
/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
|
||||
//finalize_monster_hunters() Disabled for now
|
||||
if(!report)
|
||||
report = !CONFIG_GET(flag/no_intercept_report)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/display_roundstart_logout_report), ROUNDSTART_LOGOUT_REPORT_TIME)
|
||||
@@ -305,48 +306,88 @@
|
||||
// The odds become:
|
||||
// Player A: 150 / 250 = 0.6 = 60%
|
||||
// Player B: 100 / 250 = 0.4 = 40%
|
||||
/datum/game_mode/proc/antag_pick(list/datum/candidates)
|
||||
|
||||
//Use return list if you want a list, with the arg being the number you want returned.
|
||||
//WARNING: THIS PROC DOES NOT TAKE INTO ACCOUNT WHAT SSPersistence ALREADY HAS FOR "ADJUST ANTAG REP". If this is used more than once
|
||||
//and the person rolls more than once, they will not get even more deduction!
|
||||
//More efficient if you use return list instead of calling this multiple times
|
||||
//fail_default_pick makes it use pick() instead of antag rep if it can't find anyone
|
||||
//allow_zero_if_insufficient allows it to pick people with zero rep if there isn't enough antags
|
||||
/datum/game_mode/proc/antag_pick(list/datum/mind/candidates, return_list = FALSE, fail_default_pick = TRUE, allow_zero_if_insufficient = TRUE)
|
||||
if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1)
|
||||
return pick(candidates)
|
||||
|
||||
// Tickets start at 100
|
||||
var/DEFAULT_ANTAG_TICKETS = CONFIG_GET(number/default_antag_tickets)
|
||||
//whoever named the config entries is a bad person :(
|
||||
|
||||
// You may use up to 100 extra tickets (double your odds)
|
||||
var/MAX_TICKETS_PER_ROLL = CONFIG_GET(number/max_tickets_per_roll)
|
||||
|
||||
|
||||
var/total_tickets = 0
|
||||
|
||||
MAX_TICKETS_PER_ROLL += DEFAULT_ANTAG_TICKETS
|
||||
|
||||
var/p_ckey
|
||||
var/p_rep
|
||||
|
||||
for(var/datum/mind/mind in candidates)
|
||||
p_ckey = ckey(mind.key)
|
||||
total_tickets += min(SSpersistence.antag_rep[p_ckey] + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
|
||||
|
||||
var/antag_select = rand(1,total_tickets)
|
||||
var/current = 1
|
||||
|
||||
for(var/datum/mind/mind in candidates)
|
||||
p_ckey = ckey(mind.key)
|
||||
p_rep = SSpersistence.antag_rep[p_ckey]
|
||||
|
||||
var/previous = current
|
||||
var/spend = min(p_rep + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
|
||||
current += spend
|
||||
|
||||
if(antag_select >= previous && antag_select <= (current-1))
|
||||
SSpersistence.antag_rep_change[p_ckey] = -(spend - DEFAULT_ANTAG_TICKETS)
|
||||
|
||||
// WARNING("AR_DEBUG: Player [mind.key] won spending [spend] tickets from starting value [SSpersistence.antag_rep[p_ckey]]")
|
||||
|
||||
return mind
|
||||
|
||||
WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()")
|
||||
return pick(candidates)
|
||||
//Tickets you get for free
|
||||
var/free_tickets = CONFIG_GET(number/default_antag_tickets)
|
||||
//Max extra tickets you can use
|
||||
var/additional_tickets = CONFIG_GET(number/max_tickets_per_roll)
|
||||
|
||||
var/list/ckey_to_mind = list() //this is admittedly shitcode but I'm webediting
|
||||
var/list/prev_tickets = SSpersistence.antag_rep //cache for hyper-speed in theory. how many tickets someone has stored
|
||||
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
|
||||
var/list/datum/mind/insufficient = list() //who got cucked out of an antag roll due to not having *any* tickets
|
||||
for(var/datum/mind/M in candidates)
|
||||
var/mind_ckey = ckey(M.key)
|
||||
var/can_spend = min(prev_tickets[mind_ckey], additional_tickets) //they can only spend up to config/max_tickets_per_roll
|
||||
var/amount = can_spend + free_tickets //but they get config/default_antag_tickets for free
|
||||
if(amount <= 0) //if they don't have any
|
||||
insufficient += M //too bad!
|
||||
continue
|
||||
curr_tickets[mind_ckey] = amount
|
||||
ckey_to_mind[mind_ckey] = M //make sure we can look them up after picking
|
||||
|
||||
if(!return_list) //return a single guy
|
||||
var/ckey
|
||||
if(length(curr_tickets))
|
||||
ckey = pickweight(curr_tickets)
|
||||
SSpersistence.antag_rep_change[ckey] = -(curr_tickets[ckey] - free_tickets) //deduct what they spent
|
||||
var/mind = ckey_to_mind[ckey] || (allow_zero_if_insufficient? pick(insufficient) : null) //we want their mind
|
||||
if(!mind) //no mind
|
||||
var/warning = "WARNING: No antagonists were successfully picked by /datum/gamemode/proc/antag_pick()![fail_default_pick? " Defaulting to pick()!":""]"
|
||||
message_admins(warning)
|
||||
log_game(warning)
|
||||
if(fail_default_pick)
|
||||
mind = pick(candidates)
|
||||
return mind
|
||||
else //the far more efficient and proper use of this, to get a list
|
||||
var/list/rolled = list()
|
||||
var/list/spend_tickets = list()
|
||||
for(var/i in 1 to return_list)
|
||||
if(!length(curr_tickets)) //ah heck, we're out of candidates..
|
||||
break
|
||||
var/ckey = pickweight(curr_tickets) //pick
|
||||
rolled += ckey //add
|
||||
spend_tickets[ckey] = curr_tickets[ckey] - free_tickets
|
||||
curr_tickets -= ckey //don't roll them again
|
||||
var/missing = return_list - length(rolled)
|
||||
var/list/add
|
||||
if((missing > 0) && allow_zero_if_insufficient) //need more..
|
||||
for(var/i in 1 to missing)
|
||||
if(!length(insufficient))
|
||||
break //still not enough
|
||||
var/datum/mind/M = pick_n_take(insufficient)
|
||||
add += M
|
||||
if(!length(rolled) && !length(add)) //if no one could normally roll AND no one can zero roll
|
||||
var/warning = "WARNING: No antagonists were successfully picked by /datum/gamemode/proc/antag_pick()![fail_default_pick? " Defaulting to pick()!":""]"
|
||||
message_admins(warning)
|
||||
log_game(warning)
|
||||
var/list/failed = list()
|
||||
if(fail_default_pick)
|
||||
var/list/C = candidates.Copy()
|
||||
for(var/i in 1 to return_list)
|
||||
if(!length(C))
|
||||
break
|
||||
failed += pick_n_take(C)
|
||||
return failed //Wew, no one qualified!
|
||||
for(var/i in 1 to length(rolled))
|
||||
var/ckey = rolled[i]
|
||||
SSpersistence.antag_rep_change[ckey] = -(spend_tickets[ckey]) //deduct what all of the folks who rolled spent
|
||||
rolled[i] = ckey_to_mind[ckey] //whoever called us wants minds, not ckeys
|
||||
if(add)
|
||||
rolled += add
|
||||
return rolled
|
||||
|
||||
/datum/game_mode/proc/get_players_for_role(role)
|
||||
var/list/players = list()
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
#define DOM_BLOCKED_SPAM_CAP 6
|
||||
//32 instead of 40 for safety reasons. How many turfs aren't walls around dominator for it to work
|
||||
//Update ppl somehow fuckup at 32, now we are down to 25. I hope to god they don't try harder to wall it.
|
||||
#define DOM_REQUIRED_TURFS 25
|
||||
#define DOM_HULK_HITS_REQUIRED 10
|
||||
|
||||
/obj/machinery/dominator
|
||||
name = "dominator"
|
||||
desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
|
||||
icon = 'icons/obj/machines/dominator.dmi'
|
||||
icon_state = "dominator"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
layer = HIGH_OBJ_LAYER
|
||||
max_integrity = 300
|
||||
integrity_failure = 100
|
||||
armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 10, "acid" = 70)
|
||||
var/datum/team/gang/gang
|
||||
var/operating = FALSE //false=standby or broken, true=takeover
|
||||
var/warned = FALSE //if this device has set off the warning at <3 minutes yet
|
||||
var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate
|
||||
var/datum/effect_system/spark_spread/spark_system
|
||||
var/obj/effect/countdown/dominator/countdown
|
||||
|
||||
/obj/machinery/dominator/Initialize()
|
||||
. = ..()
|
||||
set_light(l_range = 2, l_power = 0.75)
|
||||
GLOB.poi_list |= src
|
||||
spark_system = new
|
||||
spark_system.set_up(5, TRUE, src)
|
||||
countdown = new(src)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/dominator/Destroy()
|
||||
if(!(stat & BROKEN))
|
||||
set_broken()
|
||||
GLOB.poi_list.Remove(src)
|
||||
gang = null
|
||||
QDEL_NULL(spark_system)
|
||||
QDEL_NULL(countdown)
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dominator/emp_act(severity)
|
||||
take_damage(100, BURN, "energy", 0)
|
||||
..()
|
||||
|
||||
/obj/machinery/dominator/hulk_damage()
|
||||
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
|
||||
|
||||
/obj/machinery/dominator/tesla_act()
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/dominator/update_icon()
|
||||
cut_overlays()
|
||||
if(stat & BROKEN)
|
||||
icon_state = "dominator-broken"
|
||||
return
|
||||
icon_state = "dominator"
|
||||
if(operating)
|
||||
var/mutable_appearance/dominator_overlay = mutable_appearance('icons/obj/machines/dominator.dmi', "dominator-overlay")
|
||||
if(gang)
|
||||
dominator_overlay.color = gang.color
|
||||
add_overlay(dominator_overlay)
|
||||
if(obj_integrity/max_integrity < 0.66)
|
||||
add_overlay("damage")
|
||||
|
||||
/obj/machinery/dominator/examine(mob/user)
|
||||
. = ..()
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
if(gang.domination_time > world.time)
|
||||
. += "<span class='notice'>Hostile Takeover in progress. Estimated [gang.domination_time_remaining()] seconds remain.</span>"
|
||||
else
|
||||
. += "<span class='notice'>Hostile Takeover of [station_name()] successful. Have a great day.</span>"
|
||||
else
|
||||
. += "<span class='notice'>System on standby.</span>"
|
||||
. += "<span class='danger'>System Integrity: [round((obj_integrity/max_integrity)*100,1)]%</span>"
|
||||
|
||||
/obj/machinery/dominator/process()
|
||||
..()
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
var/time_remaining = gang.domination_time_remaining()
|
||||
if(time_remaining > 0)
|
||||
if(!is_station_level(z))
|
||||
explosion(src, 5, 10, 20, 30) //you now get a nice explosion if this moves off station.
|
||||
qdel(src) //to make sure it doesn't continue to exist.
|
||||
if(excessive_walls_check())
|
||||
gang.domination_time += 20
|
||||
if(spam_prevention < DOM_BLOCKED_SPAM_CAP)
|
||||
spam_prevention++
|
||||
else
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) // Play sound buzz-two.ogg, not before cause its annoying.
|
||||
gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!")
|
||||
say("Error: Takeover signal is currently blocked! There are too many walls within 3 standard units of this device.")
|
||||
spam_prevention = 0
|
||||
return
|
||||
. = TRUE
|
||||
playsound(loc, 'sound/items/timer.ogg', 10, 0)
|
||||
if(!warned && (time_remaining < 180))
|
||||
warned = TRUE
|
||||
var/area/domloc = get_area(loc)
|
||||
gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/tempgang = G
|
||||
if(tempgang != gang)
|
||||
tempgang.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
|
||||
else
|
||||
Cinematic(CINEMATIC_MALF,world) //Here is the gang victory trigger on the dominator ending.
|
||||
gang.winner = TRUE
|
||||
SSticker.news_report = GANG_VICTORY
|
||||
SSticker.force_ending = TRUE
|
||||
|
||||
if(!.)
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(src, 'sound/effects/bang.ogg', 50, 1)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
if(BURN)
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
|
||||
|
||||
/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(obj_integrity/max_integrity > 0.66)
|
||||
if(prob(damage_amount*2))
|
||||
spark_system.start()
|
||||
else if(!(stat & BROKEN))
|
||||
spark_system.start()
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/dominator/obj_break(damage_flag)
|
||||
if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1))
|
||||
set_broken()
|
||||
|
||||
/obj/machinery/dominator/deconstruct(disassembled = TRUE)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
if(!(stat & BROKEN))
|
||||
set_broken()
|
||||
new /obj/item/stack/sheet/plasteel(src.loc)
|
||||
qdel(src)
|
||||
|
||||
/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user)
|
||||
add_fingerprint(user)
|
||||
..()
|
||||
|
||||
/obj/machinery/dominator/attack_hand(mob/user)
|
||||
if(operating || (stat & BROKEN))
|
||||
examine(user)
|
||||
return
|
||||
|
||||
var/datum/team/gang/tempgang
|
||||
|
||||
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(GA)
|
||||
tempgang = GA.gang
|
||||
if(!tempgang)
|
||||
examine(user)
|
||||
return
|
||||
|
||||
if(tempgang.domination_time != NOT_DOMINATING)
|
||||
to_chat(user, "<span class='warning'>Error: Hostile Takeover is already in progress.</span>")
|
||||
return
|
||||
|
||||
if(!tempgang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
|
||||
var/time = round(tempgang.determine_domination_time()/60,0.1)
|
||||
if(alert(user,"A takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready")
|
||||
if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
|
||||
return 0
|
||||
|
||||
var/area/A = get_area(loc)
|
||||
var/locname = A.map_name
|
||||
|
||||
gang = tempgang
|
||||
gang.dom_attempts --
|
||||
priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert")
|
||||
gang.domination()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
name = "[gang.name] Gang [name]"
|
||||
operating = TRUE
|
||||
update_icon()
|
||||
|
||||
countdown.start()
|
||||
countdown.color = gang.color
|
||||
|
||||
set_light(l_range = 3, l_power = 0.9)
|
||||
light_color = gang.color
|
||||
START_PROCESSING(SSmachines, src)
|
||||
|
||||
gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]")
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/vagos = G
|
||||
if(vagos != gang)
|
||||
vagos.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
|
||||
|
||||
/obj/machinery/dominator/proc/excessive_walls_check() // why the fuck was this even a global proc...
|
||||
var/open = 0
|
||||
for(var/turf/T in view(3, src))
|
||||
if(!iswallturf(T)) //Check for /closed/wall, isclosedturf() moves it back to just checking for /closed/ which makes it very finicky.
|
||||
open++
|
||||
//to_chat(world, "THE DOMINATOR SEES [open] OPEN TURFS") uncomment to see what this shitty fucking wallcheck sees
|
||||
if(open < DOM_REQUIRED_TURFS)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
/obj/machinery/dominator/proc/set_broken()
|
||||
if(gang)
|
||||
gang.domination_time = NOT_DOMINATING
|
||||
|
||||
var/takeover_in_progress = FALSE
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/ballas = G
|
||||
if(ballas.domination_time != NOT_DOMINATING)
|
||||
takeover_in_progress = TRUE
|
||||
break
|
||||
if(!takeover_in_progress)
|
||||
var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED
|
||||
if(!was_stranded)
|
||||
priority_announce("All hostile activity within station systems has ceased.","Network Alert")
|
||||
|
||||
if(get_security_level() == "delta")
|
||||
set_security_level("red")
|
||||
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
|
||||
|
||||
set_light(0)
|
||||
operating = FALSE
|
||||
stat |= BROKEN
|
||||
update_icon()
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
#undef DOM_BLOCKED_SPAM_CAP
|
||||
#undef DOM_REQUIRED_TURFS
|
||||
#undef DOM_HULK_HITS_REQUIRED
|
||||
@@ -0,0 +1,13 @@
|
||||
/obj/effect/countdown/dominator
|
||||
name = "dominator countdown"
|
||||
text_size = 1
|
||||
color = "#e5e5e5" // Overwritten when the dominator starts
|
||||
|
||||
/obj/effect/countdown/dominator/get_value()
|
||||
var/obj/machinery/dominator/D = attached_to
|
||||
if(!istype(D))
|
||||
return
|
||||
else if(D.gang && D.gang.domination_time != NOT_DOMINATING)
|
||||
return D.gang.domination_time_remaining()
|
||||
else
|
||||
return "OFFLINE"
|
||||
@@ -0,0 +1,477 @@
|
||||
/datum/antagonist/gang
|
||||
name = "Gangster"
|
||||
roundend_category = "gangsters"
|
||||
can_coexist_with_others = FALSE
|
||||
job_rank = ROLE_GANG
|
||||
antagpanel_category = "Gang"
|
||||
var/hud_type = "gangster"
|
||||
var/message_name = "Gangster"
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/datum/antagonist/gang/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/gang/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_added(M)
|
||||
|
||||
/datum/antagonist/gang/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_removed(M)
|
||||
|
||||
/datum/antagonist/gang/get_team()
|
||||
return gang
|
||||
|
||||
/datum/antagonist/gang/greet()
|
||||
gang.greet_gangster(owner)
|
||||
|
||||
/datum/antagonist/gang/farewell()
|
||||
if(ishuman(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>You are no longer a gangster!</span>")
|
||||
|
||||
/datum/antagonist/gang/on_gain()
|
||||
if(!gang)
|
||||
create_team()
|
||||
..()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H))
|
||||
if(owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
add_to_gang()
|
||||
|
||||
/datum/antagonist/gang/on_removal()
|
||||
remove_from_gang()
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/create_team(team)
|
||||
if(!gang) // add_antag_datum calls create_team, so we need to avoid generating two gangs in that case
|
||||
if(team)
|
||||
gang = team
|
||||
return
|
||||
var/datum/team/gang/gangteam = pick_n_take(GLOB.possible_gangs)
|
||||
if(gangteam)
|
||||
gang = new gangteam
|
||||
|
||||
/datum/antagonist/gang/proc/equip_gang() // Bosses get equipped with their tools
|
||||
return
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(!ganghud)
|
||||
ganghud = new/datum/atom_hud/antag/gang()
|
||||
gang.hud_entry_num = GLOB.huds.len+1 // this is the index the gang hud will be added at
|
||||
GLOB.huds += ganghud
|
||||
ganghud.color = gang.color
|
||||
ganghud.join_hud(M)
|
||||
set_antag_hud(M,hud_type)
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(ganghud)
|
||||
ganghud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/gang/proc/can_be_converted(mob/living/candidate)
|
||||
if(!candidate.mind)
|
||||
return FALSE
|
||||
if(!can_be_owned(candidate.mind))
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = candidate
|
||||
if(!istype(H)) //Can't nonhumans
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/promote() // Bump up to boss
|
||||
var/datum/team/gang/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
owner.remove_antag_datum(/datum/antagonist/gang)
|
||||
var/datum/antagonist/gang/boss/lieutenant/new_boss = new
|
||||
new_boss.silent = TRUE
|
||||
old_owner.add_antag_datum(new_boss,old_gang)
|
||||
new_boss.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been promoted to Lieutenant in the [old_gang.name] Gang")
|
||||
to_chat(old_owner, "<FONT size=3 color=red><B>You have been promoted to Lieutenant!</B></FONT>")
|
||||
|
||||
|
||||
// Admin commands
|
||||
/datum/antagonist/gang/get_admin_commands()
|
||||
. = ..()
|
||||
.["Promote"] = CALLBACK(src,.proc/admin_promote)
|
||||
.["Set Influence"] = CALLBACK(src, .proc/admin_adjust_influence)
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
.["Set domination time left"] = CALLBACK(src, .proc/set_dom_time_left)
|
||||
|
||||
/datum/antagonist/gang/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/new_or_existing = input(admin, "Which gang do you want to be assigned to the user?", "Gangs") as null|anything in list("New","Existing")
|
||||
if(isnull(new_or_existing))
|
||||
return
|
||||
else if(new_or_existing == "New")
|
||||
var/newgang = input(admin, "Select a gang, or select random to pick a random one.", "New gang") as null|anything in GLOB.possible_gangs + "Random"
|
||||
if(isnull(newgang))
|
||||
return
|
||||
else if(newgang == "Random")
|
||||
var/datum/team/gang/G = pick_n_take(GLOB.possible_gangs)
|
||||
gang = new G
|
||||
else
|
||||
GLOB.possible_gangs -= newgang
|
||||
gang = new newgang
|
||||
else
|
||||
if(!GLOB.gangs.len) // no gangs exist
|
||||
to_chat(admin, "<span class='danger'>No gangs exist, please create a new one instead.</span>")
|
||||
return
|
||||
var/existinggang = input(admin, "Select a gang, or select random to pick a random one.", "Existing gang") as null|anything in GLOB.gangs + "Random"
|
||||
if(isnull(existinggang))
|
||||
return
|
||||
else if(existinggang == "Random")
|
||||
gang = pick(GLOB.gangs)
|
||||
else
|
||||
gang = existinggang
|
||||
..()
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/admin_promote(mob/admin)
|
||||
message_admins("[key_name_admin(admin)] has promoted [owner] to gang boss.")
|
||||
log_admin("[key_name(admin)] has promoted [owner] to boss.")
|
||||
promote()
|
||||
|
||||
/datum/antagonist/gang/proc/admin_adjust_influence()
|
||||
var/inf = input("Influence for [gang.name]","Gang influence", gang.influence) as null | num
|
||||
if(!isnull(inf))
|
||||
gang.influence = inf
|
||||
message_admins("[key_name_admin(usr)] changed [gang.name]'s influence to [inf].")
|
||||
log_admin("[key_name(usr)] changed [gang.name]'s influence to [inf].")
|
||||
|
||||
/datum/antagonist/gang/proc/add_to_gang()
|
||||
gang.add_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been converted to the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/remove_from_gang()
|
||||
gang.remove_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been deconverted from the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/set_dom_time_left(mob/admin)
|
||||
if(gang.domination_time == NOT_DOMINATING)
|
||||
return // an admin shouldn't need this
|
||||
var/seconds = input(admin, "Set the time left for the gang to win, in seconds", "Domination time left") as null|num
|
||||
if(seconds && seconds > 0)
|
||||
gang.domination_time = world.time + seconds*10
|
||||
gang.message_gangtools("Takeover shortened to [gang.domination_time_remaining()] seconds by your Syndicate benefactors.")
|
||||
|
||||
// Boss type. Those can use gang tools to buy items for their gang, in particular the Dominator, used to win the gamemode, along with more gang tools to promote fellow gangsters to boss status.
|
||||
/datum/antagonist/gang/boss
|
||||
name = "Gang boss"
|
||||
hud_type = "gang_boss"
|
||||
message_name = "Leader"
|
||||
|
||||
/datum/antagonist/gang/boss/on_gain()
|
||||
..()
|
||||
if(gang)
|
||||
gang.leaders += owner
|
||||
|
||||
/datum/antagonist/gang/boss/on_removal()
|
||||
if(gang)
|
||||
gang.leaders -= owner
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/boss/antag_listing_name()
|
||||
return ..() + "(Boss)"
|
||||
|
||||
/datum/antagonist/gang/boss/equip_gang(gangtool = TRUE, pen = TRUE, spraycan = TRUE, hud = TRUE) // usually has to be called separately
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = SLOT_IN_BACKPACK,
|
||||
"left pocket" = SLOT_L_STORE,
|
||||
"right pocket" = SLOT_R_STORE,
|
||||
"hands" = SLOT_HANDS
|
||||
)
|
||||
|
||||
if(gangtool)//Here is where all of the text occurs when a gang boss first spawns in.
|
||||
var/obj/item/device/gangtool/G = new()
|
||||
var/where = H.equip_in_one_of_slots(G, slots)
|
||||
if (!where)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.")
|
||||
else
|
||||
G.register_device(H)
|
||||
to_chat(H, "The <b>Gangtool</b> in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station.")
|
||||
to_chat(H, "As the gang boss, you can also promote your gang members to <b>lieutenant</b>. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use gangtools too.")
|
||||
|
||||
if(pen)
|
||||
var/obj/item/pen/gang/T = new()
|
||||
var/where2 = H.equip_in_one_of_slots(T, slots)
|
||||
if (!where2)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.")
|
||||
else
|
||||
to_chat(H, "The <b>recruitment pen</b> in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them. All gangsters can use these, distribute them to see your gang grow.")
|
||||
|
||||
if(spraycan)
|
||||
var/obj/item/toy/crayon/spraycan/gang/SC = new(null,gang)
|
||||
var/where3 = H.equip_in_one_of_slots(SC, slots)
|
||||
if (!where3)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.")
|
||||
else
|
||||
to_chat(H, "The <b>territory spraycan</b> in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster.")
|
||||
|
||||
if(hud)
|
||||
var/obj/item/clothing/glasses/hud/security/chameleon/C = new(null,gang)
|
||||
var/where4 = H.equip_in_one_of_slots(C, slots)
|
||||
if (!where4)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.")
|
||||
else
|
||||
to_chat(H, "The <b>chameleon security HUD</b> in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.")
|
||||
|
||||
// Admin commands for bosses
|
||||
/datum/antagonist/gang/boss/admin_add(datum/mind/new_owner,mob/admin)
|
||||
if(!new_owner.has_antag_datum(parent_type))
|
||||
..()
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
return
|
||||
promote()
|
||||
message_admins("[key_name_admin(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
log_admin("[key_name(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/get_admin_commands()
|
||||
. = ..()
|
||||
. -= "Promote"
|
||||
.["Take gangtool"] = CALLBACK(src,.proc/admin_take_gangtool)
|
||||
.["Give gangtool"] = CALLBACK(src,.proc/admin_give_gangtool)
|
||||
.["Demote"] = CALLBACK(src,.proc/admin_demote)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/demote()
|
||||
var/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
silent = TRUE
|
||||
owner.remove_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/datum/antagonist/gang/new_gangster = new /datum/antagonist/gang()
|
||||
new_gangster.silent = TRUE
|
||||
old_owner.add_antag_datum(new_gangster,old_gang)
|
||||
new_gangster.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been demoted to Gangster in the [gang.name] Gang")
|
||||
to_chat(old_owner, "<span class='userdanger'>The gang has been disappointed of your leader traits! You are a regular gangster now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_take_gangtool(mob/admin)
|
||||
var/list/L = owner.current.get_contents()
|
||||
var/obj/item/device/gangtool/gangtool = locate() in L
|
||||
if (!gangtool)
|
||||
to_chat(admin, "<span class='danger'>Deleting gangtool failed!</span>")
|
||||
return
|
||||
qdel(gangtool)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_give_gangtool(mob/admin)
|
||||
equip_gang(TRUE, FALSE, FALSE, FALSE)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_demote(datum/mind/target,mob/user)
|
||||
message_admins("[key_name_admin(user)] has demoted [owner.current] from gang boss.")
|
||||
log_admin("[key_name(user)] has demoted [owner.current] from gang boss.")
|
||||
admin_take_gangtool(user)
|
||||
demote()
|
||||
|
||||
/datum/antagonist/gang/boss/lieutenant
|
||||
name = "Gang Lieutenant"
|
||||
message_name = "Lieutenant"
|
||||
hud_type = "gang_lt"
|
||||
|
||||
#define MAXIMUM_RECALLS 3
|
||||
#define INFLUENCE_INTERVAL 1200 //This handles the interval between each count of influence.
|
||||
// Gang team datum. This handles the gang itself.
|
||||
/datum/team/gang
|
||||
name = "Gang"
|
||||
member_name = "gangster"
|
||||
var/hud_entry_num // because if you put something other than a number in GLOB.huds, god have mercy on your fucking soul friend
|
||||
var/list/leaders = list() // bosses
|
||||
var/max_leaders = MAX_LEADERS_GANG
|
||||
var/list/territories = list() // territories owned by the gang.
|
||||
var/list/lost_territories = list() // territories lost by the gang.
|
||||
var/list/new_territories = list() // territories captured by the gang.
|
||||
var/list/gangtools = list()
|
||||
var/domination_time = NOT_DOMINATING
|
||||
var/dom_attempts = INITIAL_DOM_ATTEMPTS
|
||||
var/color
|
||||
var/influence = 0 // influence of the gang, based on how many territories they own. Can be used to buy weapons and tools from a gang uplink.
|
||||
var/winner // Once the gang wins with a dominator, this becomes true. For roundend credits purposes.
|
||||
var/list/inner_outfits = list()
|
||||
var/list/outer_outfits = list()
|
||||
var/next_point_time
|
||||
var/recalls = MAXIMUM_RECALLS // Once this reaches 0, this gang cannot force recall the shuttle with their gangtool anymore
|
||||
|
||||
/datum/team/gang/New(starting_members)
|
||||
. = ..()
|
||||
GLOB.gangs += src
|
||||
if(starting_members)
|
||||
if(islist(starting_members))
|
||||
for(var/datum/mind/groveboss in starting_members)
|
||||
leaders += groveboss
|
||||
var/datum/antagonist/gang/boss/gb = new
|
||||
groveboss.add_antag_datum(gb, src)
|
||||
gb.equip_gang()
|
||||
|
||||
else
|
||||
var/datum/mind/CJ = starting_members
|
||||
if(istype(CJ))
|
||||
leaders += CJ
|
||||
var/datum/antagonist/gang/boss/bossdatum = new
|
||||
CJ.add_antag_datum(bossdatum, src)
|
||||
bossdatum.equip_gang()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/Destroy()
|
||||
GLOB.gangs -= src
|
||||
..()
|
||||
|
||||
/datum/team/gang/roundend_report() //roundend report.
|
||||
var/list/report = list()
|
||||
report += "<span class='header'>[name]:</span>"
|
||||
if(winner)
|
||||
report += "<span class='greentext'>The [name] gang successfully activated the mind dominator!</span>"
|
||||
else
|
||||
report += "<span class='redtext'>The [name] gang has failed!</span>"
|
||||
|
||||
report += "The [name] gang bosses were:"
|
||||
report += printplayerlist(leaders)
|
||||
report += "The [name] [member_name]s were:"
|
||||
report += printplayerlist(members-leaders)
|
||||
|
||||
return "<div class='panel redborder'>[report.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/gang/proc/greet_gangster(datum/mind/gangster) //The text a person receives when recruited.
|
||||
var/message = "<FONT size=3 color=red><B>You are now a member of the <font color='[color]'>[name]</font> Gang!</B></FONT>"
|
||||
message += "<font color='red'>Help your bosses take over the station by claiming territory with <b>spraycans</b>. Simply spray on any unclaimed area of the station.</font>"
|
||||
message += "<font color='red'>You can also use recruitment pens to recruit more to your cause, If your boss provides you one.</font>"
|
||||
message += "<font color='red'>Their ultimate objective is to take over the station with a Dominator machine.</font>"
|
||||
message += "<font color='red'>You can identify your mates by their <b>large, <font color='[color]'> \[G\]</font> icon</b>.</font>"
|
||||
to_chat(gangster, message)
|
||||
gangster.store_memory("You are a member of the [name] Gang!")
|
||||
|
||||
/datum/team/gang/proc/handle_territories()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
if(!leaders.len)
|
||||
return
|
||||
var/added_names = ""
|
||||
var/lost_names = ""
|
||||
|
||||
//Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report
|
||||
var/list/reclaimed_territories = new_territories & lost_territories
|
||||
territories |= reclaimed_territories
|
||||
new_territories -= reclaimed_territories
|
||||
lost_territories -= reclaimed_territories
|
||||
|
||||
//Process lost territories
|
||||
for(var/area in lost_territories)
|
||||
if(lost_names != "")
|
||||
lost_names += ", "
|
||||
lost_names += "[lost_territories[area]]"
|
||||
territories -= area
|
||||
|
||||
//Calculate and report influence growth
|
||||
|
||||
//Process new territories
|
||||
for(var/area in new_territories)
|
||||
if(added_names != "")
|
||||
added_names += ", "
|
||||
added_names += "[new_territories[area]]"
|
||||
territories += area
|
||||
|
||||
//Report territory changes
|
||||
var/message = "<b>[src] Gang Status Report:</b>.<BR>*---------*<BR>"
|
||||
message += "<b>[new_territories.len] new territories:</b><br><i>[added_names]</i><br>"
|
||||
message += "<b>[lost_territories.len] territories lost:</b><br><i>[lost_names]</i><br>"
|
||||
//Clear the lists
|
||||
new_territories = list()
|
||||
lost_territories = list()
|
||||
var/total_territories = total_claimable_territories()
|
||||
var/control = round((territories.len/total_territories)*100, 1)
|
||||
var/uniformed = check_clothing()
|
||||
message += "Your gang now has <b>[control]% control</b> of the station.<BR>*---------*<BR>"
|
||||
if(domination_time != NOT_DOMINATING)
|
||||
var/new_time = max(world.time, domination_time - (uniformed * 4) - (territories.len * 2))
|
||||
if(new_time < domination_time)
|
||||
message += "Takeover shortened by [(domination_time - new_time)*0.1] seconds for defending [territories.len] territories.<BR>"
|
||||
domination_time = new_time
|
||||
message += "<b>[domination_time_remaining()] seconds remain</b> in hostile takeover.<BR>"
|
||||
else
|
||||
var/new_influence = check_territory_income()
|
||||
if(new_influence != influence)
|
||||
message += "Gang influence has increased by [new_influence - influence] for defending [territories.len] territories and [uniformed] uniformed gangsters.<BR>"
|
||||
influence = new_influence
|
||||
message += "Your gang now has <b>[influence] influence</b>.<BR>"
|
||||
message_gangtools(message)
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/proc/total_claimable_territories()
|
||||
var/list/valid_territories = list()
|
||||
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) //First, collect all area types on the station zlevel
|
||||
for(var/ar in SSmapping.areas_in_z["[z]"])
|
||||
var/area/A = ar
|
||||
if(!(A.type in valid_territories) && A.valid_territory)
|
||||
valid_territories |= A.type
|
||||
return valid_territories.len
|
||||
|
||||
/datum/team/gang/proc/check_territory_income()
|
||||
var/new_influence = min(999,influence + 15 + (check_clothing() * 2) + territories.len)
|
||||
return new_influence
|
||||
|
||||
/datum/team/gang/proc/check_clothing()
|
||||
//Count uniformed gangsters
|
||||
var/uniformed = 0
|
||||
for(var/datum/mind/gangmind in members)
|
||||
if(ishuman(gangmind.current))
|
||||
var/mob/living/carbon/human/gangster = gangmind.current
|
||||
//Gangster must be alive and should return 0 not continue if conditions are met.
|
||||
if(!istype(gangster) || gangster.stat == DEAD)
|
||||
return 0
|
||||
|
||||
var/obj/item/clothing/outfit
|
||||
var/obj/item/clothing/gang_outfit
|
||||
if(gangster.w_uniform)
|
||||
outfit = gangster.w_uniform
|
||||
if(outfit.type in inner_outfits)
|
||||
gang_outfit = outfit
|
||||
if(gangster.wear_suit)
|
||||
outfit = gangster.wear_suit
|
||||
if(outfit.type in outer_outfits)
|
||||
gang_outfit = outfit
|
||||
|
||||
if(gang_outfit)
|
||||
uniformed++
|
||||
return uniformed
|
||||
|
||||
/datum/team/gang/proc/adjust_influence(value)
|
||||
influence = max(0, influence + value)
|
||||
|
||||
/datum/team/gang/proc/message_gangtools(message)
|
||||
if(!gangtools.len || !message)
|
||||
return
|
||||
for(var/i in gangtools)
|
||||
var/obj/item/device/gangtool/tool = i
|
||||
var/mob/living/mob = get(tool.loc, /mob/living)
|
||||
if(mob && mob.mind && mob.stat == CONSCIOUS)
|
||||
var/datum/antagonist/gang/gangster = mob.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(gangster.gang == src)
|
||||
to_chat(mob, "<span class='warning'>[icon2html(tool, mob)] [message]</span>")
|
||||
playsound(mob.loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
return
|
||||
|
||||
/datum/team/gang/proc/domination()
|
||||
domination_time = world.time + determine_domination_time()*10
|
||||
set_security_level("delta")
|
||||
|
||||
/datum/team/gang/proc/determine_domination_time() // calculates the value in seconds (this is the initial domination time!)
|
||||
var/total_territories = total_claimable_territories()
|
||||
return max(180,480 - (round((territories.len/total_territories)*100, 1) * 9))
|
||||
|
||||
/datum/team/gang/proc/domination_time_remaining() // retrieves the value from world.time based deciseconds to seconds
|
||||
var/diff = domination_time - world.time
|
||||
return round(diff * 0.1)
|
||||
|
||||
|
||||
#undef MAXIMUM_RECALLS
|
||||
#undef INFLUENCE_INTERVAL
|
||||
@@ -0,0 +1,139 @@
|
||||
// Gang datums go here. If you want to create a new gang, you must be sure to edit:
|
||||
// name
|
||||
// color (must be a hex, "blue" isn't acceptable due to how spraycans are handled)
|
||||
// inner_outfits (must be a list() with typepaths of the clothes in it. One is fine, but there is support for multiple: one will be picked at random when bought)
|
||||
// outer_outfits (same as above)
|
||||
// You also need to make a gang graffiti, that will go in crayondecal.dmi inside our icons, with the same name of the gang it's assigned to. Nothing else,just the icon.
|
||||
// Those are all required. If one is missed, stuff could break.
|
||||
|
||||
/datum/team/gang/clandestine
|
||||
name = "Clandestine"
|
||||
color = "#FF0000"
|
||||
inner_outfits = list(/obj/item/clothing/under/syndicate/combat)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket)
|
||||
|
||||
/datum/team/gang/prima
|
||||
name = "Prima"
|
||||
color = "#FFFF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/yellow)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hastur)
|
||||
|
||||
/datum/team/gang/zerog
|
||||
name = "Zero-G"
|
||||
color = "#C0C0C0"
|
||||
inner_outfits = list(/obj/item/clothing/under/suit_jacket/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat)
|
||||
|
||||
/datum/team/gang/max
|
||||
name = "Max"
|
||||
color = "#800000"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/maroon)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/red)
|
||||
|
||||
/datum/team/gang/blasto
|
||||
name = "Blasto"
|
||||
color = "#000080"
|
||||
inner_outfits = list(/obj/item/clothing/under/suit_jacket/navy)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/miljacket)
|
||||
|
||||
/datum/team/gang/waffle
|
||||
name = "Waffle"
|
||||
color = "#808000" //shared color with cyber, but they can keep brown cause waffles.
|
||||
inner_outfits = list(/obj/item/clothing/under/suit_jacket/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho)
|
||||
|
||||
/datum/team/gang/north
|
||||
name = "North"
|
||||
color = "#00FF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/green)
|
||||
|
||||
/datum/team/gang/omni
|
||||
name = "Omni"
|
||||
color = "#008080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/teal)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/studentuni)
|
||||
|
||||
/datum/team/gang/newton
|
||||
name = "Newton"
|
||||
color = "#A52A2A"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/brown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
|
||||
|
||||
/datum/team/gang/cyber
|
||||
name = "Cyber"
|
||||
color = "#00f904" //Cyber and waffle shared colors, I made these guys green and made weed darker green.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightbrown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/pharaoh)
|
||||
|
||||
/datum/team/gang/donk
|
||||
name = "Donk"
|
||||
color = "#0000FF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkblue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/overalls)
|
||||
|
||||
/datum/team/gang/gene
|
||||
name = "Gene"
|
||||
color = "#00FFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/blue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron)
|
||||
|
||||
/datum/team/gang/gib
|
||||
name = "Gib"
|
||||
color = "#636060" //Applying black to grayscale... Zero-G is already grey too. oh well.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/black)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather/overcoat)
|
||||
|
||||
/datum/team/gang/tunnel
|
||||
name = "Tunnel"
|
||||
color = "#FF00FF" //Gave the leather jacket to the tunnel gang over diablo.
|
||||
inner_outfits = list(/obj/item/clothing/under/villain)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather)
|
||||
|
||||
/datum/team/gang/diablo
|
||||
name = "Diablo"
|
||||
color = "#FF0000" //literal early 90s skinhead regalia.
|
||||
inner_outfits = list(/obj/item/clothing/under/pants/classicjeans)
|
||||
outer_outfits = list(/obj/item/clothing/suit/suspenders)
|
||||
|
||||
/datum/team/gang/psyke
|
||||
name = "Psyke"
|
||||
color = "#808080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/grey)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings/griffinwings)
|
||||
|
||||
/datum/team/gang/osiron
|
||||
name = "Osiron"
|
||||
color = "#FFFFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/labcoat)
|
||||
|
||||
/datum/team/gang/sirius
|
||||
name = "Sirius"
|
||||
color = "#FFC0CB"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/pink)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/puffer/vest)
|
||||
|
||||
/datum/team/gang/sleepingcarp
|
||||
name = "Sleeping Carp"
|
||||
color = "#800080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightpurple)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/carp_costume)
|
||||
|
||||
/datum/team/gang/h
|
||||
name = "H"
|
||||
color = "#993333"
|
||||
inner_outfits = list(/obj/item/clothing/under/jabroni) //Why not?
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
|
||||
|
||||
/datum/team/gang/rigatonifamily
|
||||
name = "Rigatoni family"
|
||||
color = "#cc9900" // p a s t a colored
|
||||
inner_outfits = list(/obj/item/clothing/under/rank/chef)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/chef)
|
||||
|
||||
/datum/team/gang/weed
|
||||
name = "Weed"
|
||||
color = "#6cd648"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkgreen)
|
||||
outer_outfits = list(/obj/item/clothing/suit/vapeshirt)
|
||||
@@ -0,0 +1,38 @@
|
||||
/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
||||
. = ..()
|
||||
if(type == "poseur tag")
|
||||
var/datum/team/gang/gang = pick(subtypesof(/datum/team/gang))
|
||||
var/gangname = initial(gang.name)
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
icon_state = "[gangname]"
|
||||
type = null
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide
|
||||
plane = GAME_PLANE
|
||||
do_icon_rotate = FALSE //These are designed to always face south, so no rotation please.
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user)
|
||||
if(!G)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
gang = G
|
||||
var/newcolor = G.color
|
||||
var/area/territory = get_area(src)
|
||||
icon_state = G.name
|
||||
G.new_territories |= list(territory.type = territory.name)
|
||||
//If this isn't tagged by a specific gangster there's no bonus income.
|
||||
.=..(mapload, newcolor, icon_state, e_name, rotation)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Destroy()
|
||||
if(gang)
|
||||
var/area/territory = get_area(src)
|
||||
gang.territories -= territory.type
|
||||
gang.new_territories -= territory.type
|
||||
gang.lost_territories |= list(territory.type = territory.name)
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
|
||||
return isspaceturf(T) || islava(T) || istype(T, /turf/open/water) || ischasm(T)
|
||||
@@ -0,0 +1,34 @@
|
||||
/datum/atom_hud/antag/gang
|
||||
var/color = null
|
||||
|
||||
/datum/atom_hud/antag/gang/add_to_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/remove_from_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/join_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/leave_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
@@ -0,0 +1,417 @@
|
||||
/datum/gang_item
|
||||
var/name
|
||||
var/item_path
|
||||
var/cost
|
||||
var/spawn_msg
|
||||
var/category
|
||||
var/list/gang_whitelist = list()
|
||||
var/list/gang_blacklist = list()
|
||||
var/id
|
||||
|
||||
/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool, check_canbuy = TRUE)
|
||||
if(check_canbuy && !can_buy(user, gang, gangtool))
|
||||
return FALSE
|
||||
var/real_cost = get_cost(user, gang, gangtool)
|
||||
if(!spawn_item(user, gang, gangtool))
|
||||
gang.adjust_influence(-real_cost)
|
||||
to_chat(user, "<span class='notice'>You bought \the [name].</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) // If this returns anything other than null, something fucked up and influence won't lower.
|
||||
if(item_path)
|
||||
var/obj/item/O = new item_path(user.loc)
|
||||
user.put_in_hands(O)
|
||||
else
|
||||
return TRUE
|
||||
if(spawn_msg)
|
||||
to_chat(user, "[spawn_msg]")
|
||||
|
||||
/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return gang && (gang.influence >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool)
|
||||
|
||||
/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return cost
|
||||
|
||||
/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return "([get_cost(user, gang, gangtool)] Influence)"
|
||||
|
||||
/datum/gang_item/proc/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return name
|
||||
|
||||
/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return
|
||||
|
||||
///////////////////
|
||||
//CLOTHING
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/clothing
|
||||
category = "Purchase Gang Clothes (Only the jumpsuit and suit give you added influence):"
|
||||
|
||||
/datum/gang_item/clothing/under
|
||||
name = "Gang Uniform"
|
||||
id = "under"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/under/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.inner_outfits.len)
|
||||
var/outfit = pick(gang.inner_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official uniform, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/suit
|
||||
name = "Gang Armored Outerwear"
|
||||
id = "suit"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/suit/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.outer_outfits.len)
|
||||
var/outfit = pick(gang.outer_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
O.armor = O.armor.setRating(melee = 25, bullet = 35, laser = 15, energy = 10, bomb = 30, bio = 0, rad = 0, fire = 30, acid = 30)
|
||||
O.desc += " Tailored for the [gang.name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official outerwear, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/hat
|
||||
name = "Pimp Hat"
|
||||
id = "hat"
|
||||
cost = 16
|
||||
item_path = /obj/item/clothing/head/collectable/petehat/gang
|
||||
|
||||
/obj/item/clothing/head/collectable/petehat/gang
|
||||
name = "pimpin' hat"
|
||||
desc = "The undisputed king of style."
|
||||
|
||||
/datum/gang_item/clothing/mask
|
||||
name = "Golden Death Mask"
|
||||
id = "mask"
|
||||
cost = 18
|
||||
item_path = /obj/item/clothing/mask/gskull
|
||||
|
||||
/obj/item/clothing/mask/gskull
|
||||
name = "golden death mask"
|
||||
icon_state = "gskull"
|
||||
desc = "Strike terror, and envy, into the hearts of your enemies."
|
||||
|
||||
/datum/gang_item/clothing/shoes
|
||||
name = "Bling Boots"
|
||||
id = "boots"
|
||||
cost = 20
|
||||
item_path = /obj/item/clothing/shoes/gang
|
||||
|
||||
/obj/item/clothing/shoes/gang
|
||||
name = "blinged-out boots"
|
||||
desc = "Stand aside peasants."
|
||||
icon_state = "bling"
|
||||
|
||||
/datum/gang_item/clothing/neck
|
||||
name = "Gold Necklace"
|
||||
id = "necklace"
|
||||
cost = 9
|
||||
item_path = /obj/item/clothing/neck/necklace/dope
|
||||
|
||||
/datum/gang_item/clothing/hands
|
||||
name = "Decorative Brass Knuckles"
|
||||
id = "hand"
|
||||
cost = 11
|
||||
item_path = /obj/item/clothing/gloves/gang
|
||||
|
||||
/obj/item/clothing/gloves/gang
|
||||
name = "braggadocio's brass knuckles"
|
||||
desc = "Purely decorative, don't find out the hard way."
|
||||
icon_state = "knuckles"
|
||||
w_class = 3
|
||||
|
||||
datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways?
|
||||
name = "Cool Sunglasses"
|
||||
id = "glasses"
|
||||
cost = 5
|
||||
item_path = /obj/item/clothing/glasses/sunglasses
|
||||
|
||||
/datum/gang_item/clothing/belt
|
||||
name = "Badass Belt"
|
||||
id = "belt"
|
||||
cost = 13
|
||||
item_path = /obj/item/storage/belt/military/gang
|
||||
|
||||
/obj/item/storage/belt/military/gang
|
||||
name = "badass belt"
|
||||
icon_state = "gangbelt"
|
||||
item_state = "gang"
|
||||
desc = "The belt buckle simply reads 'BAMF'."
|
||||
|
||||
///////////////////
|
||||
//WEAPONS
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/weapon
|
||||
category = "Purchase Weapons:"
|
||||
|
||||
/datum/gang_item/weapon/ammo
|
||||
|
||||
/datum/gang_item/weapon/shuriken
|
||||
name = "Shuriken"
|
||||
id = "shuriken"
|
||||
cost = 2
|
||||
item_path = /obj/item/throwing_star
|
||||
|
||||
/datum/gang_item/weapon/switchblade
|
||||
name = "Switchblade"
|
||||
id = "switchblade"
|
||||
cost = 5
|
||||
item_path = /obj/item/switchblade
|
||||
|
||||
/datum/gang_item/weapon/surplus //For when a gang boss is extra broke or cheap.
|
||||
name = "Surplus Rifle"
|
||||
id = "surplus"
|
||||
cost = 6
|
||||
item_path = /obj/item/gun/ballistic/automatic/surplus
|
||||
|
||||
/datum/gang_item/weapon/ammo/surplus_ammo
|
||||
name = "Surplus Rifle Ammo"
|
||||
id = "surplus_ammo"
|
||||
cost = 3
|
||||
item_path = /obj/item/ammo_box/magazine/m10mm/rifle
|
||||
|
||||
/datum/gang_item/weapon/improvised
|
||||
name = "Sawn-Off Improvised Shotgun"
|
||||
id = "sawn"
|
||||
cost = 5
|
||||
item_path = /obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn
|
||||
|
||||
/datum/gang_item/weapon/ammo/improvised_ammo
|
||||
name = "Box of Buckshot"
|
||||
id = "buckshot"
|
||||
cost = 5
|
||||
item_path = /obj/item/storage/box/lethalshot
|
||||
|
||||
/datum/gang_item/weapon/pistol
|
||||
name = "10mm Pistol"
|
||||
id = "pistol"
|
||||
cost = 25
|
||||
item_path = /obj/item/gun/ballistic/automatic/pistol
|
||||
|
||||
/datum/gang_item/weapon/ammo/pistol_ammo
|
||||
name = "10mm Ammo"
|
||||
id = "pistol_ammo"
|
||||
cost = 10
|
||||
item_path = /obj/item/ammo_box/magazine/m10mm
|
||||
|
||||
/datum/gang_item/weapon/sniper
|
||||
name = "Black Market .50cal Sniper Rifle"
|
||||
id = "sniper"
|
||||
cost = 35
|
||||
item_path = /obj/item/gun/ballistic/automatic/sniper_rifle
|
||||
|
||||
/datum/gang_item/weapon/ammo/sniper_ammo
|
||||
name = "Smuggled .50cal Sniper Rounds"
|
||||
id = "sniper_ammo"
|
||||
cost = 15
|
||||
item_path = /obj/item/ammo_box/magazine/sniper_rounds
|
||||
|
||||
/datum/gang_item/weapon/ammo/sleeper_ammo
|
||||
name = "Illicit Soporific Cartridges"
|
||||
id = "sniper_ammo"
|
||||
cost = 15
|
||||
item_path = /obj/item/ammo_box/magazine/sniper_rounds/soporific
|
||||
|
||||
/datum/gang_item/weapon/machinegun
|
||||
name = "Mounted Machine Gun"
|
||||
id = "MG"
|
||||
cost = 45
|
||||
item_path = /obj/machinery/manned_turret
|
||||
spawn_msg = "<span class='notice'>The mounted machine gun features enhanced responsiveness. Hold down on the trigger while firing to control where you're shooting.</span>"
|
||||
|
||||
/datum/gang_item/weapon/machinegun/spawn_item(mob/living/carbon/user, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
|
||||
/datum/gang_item/weapon/uzi
|
||||
name = "Uzi SMG"
|
||||
id = "uzi"
|
||||
cost = 50
|
||||
item_path = /obj/item/gun/ballistic/automatic/mini_uzi
|
||||
|
||||
/datum/gang_item/weapon/ammo/uzi_ammo
|
||||
name = "Uzi Ammo"
|
||||
id = "uzi_ammo"
|
||||
cost = 20
|
||||
item_path = /obj/item/ammo_box/magazine/uzim9mm
|
||||
|
||||
///////////////////
|
||||
//EQUIPMENT
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/equipment
|
||||
category = "Purchase Equipment:"
|
||||
|
||||
/datum/gang_item/equipment/spraycan
|
||||
name = "Territory Spraycan"
|
||||
id = "spraycan"
|
||||
cost = 1
|
||||
item_path = /obj/item/toy/crayon/spraycan/gang
|
||||
|
||||
/datum/gang_item/equipment/spraycan/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
|
||||
/datum/gang_item/equipment/sharpener
|
||||
name = "Sharpener"
|
||||
id = "whetstone"
|
||||
cost = 3
|
||||
item_path = /obj/item/sharpener
|
||||
|
||||
/datum/gang_item/equipment/emp
|
||||
name = "EMP Grenade"
|
||||
id = "EMP"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/empgrenade
|
||||
|
||||
/datum/gang_item/equipment/c4
|
||||
name = "C4 Explosive"
|
||||
id = "c4"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/plastic/c4
|
||||
|
||||
/datum/gang_item/equipment/frag
|
||||
name = "Fragmentation Grenade"
|
||||
id = "frag nade"
|
||||
cost = 5
|
||||
item_path = /obj/item/grenade/syndieminibomb/concussion/frag
|
||||
|
||||
/datum/gang_item/equipment/stimpack
|
||||
name = "Black Market Stimulants"
|
||||
id = "stimpack"
|
||||
cost = 12
|
||||
item_path = /obj/item/reagent_containers/syringe/stimulants
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker
|
||||
name = "Implant Breaker"
|
||||
id = "implant_breaker"
|
||||
cost = 10
|
||||
item_path = /obj/item/implanter/gang
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'>The <b>implant breaker</b> is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters.</span>")
|
||||
|
||||
/datum/gang_item/equipment/wetwork_boots
|
||||
name = "Wetwork boots"
|
||||
id = "wetwork"
|
||||
cost = 8
|
||||
item_path = /obj/item/clothing/shoes/combat/gang
|
||||
|
||||
/obj/item/clothing/shoes/combat/gang
|
||||
name = "Wetwork boots"
|
||||
desc = "A gang's best hitmen are prepared for anything."
|
||||
permeability_coefficient = 0.01
|
||||
clothing_flags = NOSLIP
|
||||
|
||||
datum/gang_item/equipment/shield
|
||||
name = "Riot Shield"
|
||||
id = "riot_shield"
|
||||
cost = 25
|
||||
item_path = /obj/item/shield/riot
|
||||
|
||||
datum/gang_item/equipment/gangsheild
|
||||
name = "Tower Shield"
|
||||
id = "metal"
|
||||
cost = 45 //High block of melee and even higher for bullets
|
||||
item_path = /obj/item/shield/riot/tower
|
||||
|
||||
/datum/gang_item/equipment/pen
|
||||
name = "Recruitment Pen"
|
||||
id = "pen"
|
||||
cost = 20
|
||||
item_path = /obj/item/pen/gang
|
||||
spawn_msg = "<span class='notice'>More <b>recruitment pens</b> will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.</span>"
|
||||
|
||||
/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(..())
|
||||
gangtool.free_pen = FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/gang_item/equipment/pen/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gangtool && gangtool.free_pen)
|
||||
return 0
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/pen/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gangtool && gangtool.free_pen)
|
||||
return "(GET ONE FREE)"
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/gangtool
|
||||
id = "gangtool"
|
||||
cost = 5
|
||||
|
||||
/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/item_type
|
||||
if(gang)
|
||||
item_type = /obj/item/device/gangtool/spare/lt
|
||||
if(gang.leaders.len < MAX_LEADERS_GANG)
|
||||
to_chat(user, "<span class='notice'><b>Gangtools</b> allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [MAX_LEADERS_GANG-gang.leaders.len] more Lieutenants</span>")
|
||||
else
|
||||
item_type = /obj/item/device/gangtool/spare
|
||||
var/obj/item/device/gangtool/spare/tool = new item_type(user.loc)
|
||||
user.put_in_hands(tool)
|
||||
|
||||
/datum/gang_item/equipment/gangtool/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang && (gang.leaders.len < gang.max_leaders))
|
||||
return "Promote a Gangster"
|
||||
return "Spare Gangtool"
|
||||
|
||||
/datum/gang_item/equipment/dominator
|
||||
name = "Station Dominator"
|
||||
id = "dominator"
|
||||
cost = 30
|
||||
item_path = /obj/machinery/dominator
|
||||
spawn_msg = "<span class='notice'>The <b>dominator</b> will secure your gang's dominance over the station. Turn it on when you are ready to defend it.</span>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return ..()
|
||||
return "<b>[..()]</b>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return "(Out of stock)"
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang)
|
||||
return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)"
|
||||
|
||||
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/area/userarea = get_area(user)
|
||||
if(!(userarea.type in gang.territories|gang.new_territories))
|
||||
to_chat(user,"<span class='warning'>The <b>dominator</b> can be spawned only on territory controlled by your gang!</span>")
|
||||
return FALSE
|
||||
for(var/obj/obj in get_turf(user))
|
||||
if(obj.density)
|
||||
to_chat(user, "<span class='warning'>There's not enough room here!</span>")
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Gang Boss Pens
|
||||
*/
|
||||
/obj/item/pen/gang
|
||||
var/cooldown
|
||||
var/last_used
|
||||
|
||||
/obj/item/pen/gang/Initialize()
|
||||
. = ..()
|
||||
last_used = world.time
|
||||
|
||||
/obj/item/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE) //ha
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!ishuman(M) || !ishuman(user) || M.stat == DEAD)
|
||||
return ..()
|
||||
//var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) //Pen works with bosses only.
|
||||
var/datum/antagonist/gang/L = user.mind.has_antag_datum(/datum/antagonist/gang) //Pen works with anyone in gang.
|
||||
if(!L)
|
||||
return ..()
|
||||
if(!..())
|
||||
return
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src] needs more time to recharge before it can be used.</span>")
|
||||
return
|
||||
if(!M.client || !M.mind)
|
||||
to_chat(user, "<span class='warning'>A braindead gangster is an useless gangster!</span>")
|
||||
return
|
||||
var/datum/team/gang/gang = L.gang
|
||||
if(!add_gangster(user, gang, M.mind))
|
||||
return
|
||||
cooldown = TRUE
|
||||
icon_state = "pen_blink"
|
||||
var/cooldown_time = 600/gang.leaders.len
|
||||
addtimer(CALLBACK(src, .proc/cooldown), cooldown_time)
|
||||
|
||||
/obj/item/pen/gang/proc/cooldown()
|
||||
cooldown = FALSE
|
||||
icon_state = "pen"
|
||||
var/mob/M = loc
|
||||
if(istype(M))
|
||||
to_chat(M, "<span class='notice'>[icon2html(src, M)] [src][(loc == M)?(""):(" in your [loc]")] vibrates softly. It is ready to be used again.</span>")
|
||||
|
||||
/obj/item/pen/gang/proc/add_gangster(mob/user, datum/team/gang/gang, datum/mind/gangster_mind, check = TRUE) // Basically a wrapper to add_antag_datum.
|
||||
var/datum/antagonist/dudegang = gangster_mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(dudegang)
|
||||
if(dudegang == gang)
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by your gang!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by someone else!</span>")
|
||||
return
|
||||
if(check && HAS_TRAIT(gangster_mind.current, TRAIT_MINDSHIELD)) //Check to see if the potential gangster is implanted
|
||||
to_chat(user, "<span class='danger'>This mind is too strong to control!</span>")
|
||||
return
|
||||
var/mob/living/carbon/human/H = gangster_mind.current // we are sure the dude's human cause it's checked in attack()
|
||||
H.silent = max(H.silent, 5)
|
||||
H.Knockdown(100)
|
||||
gangster_mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
return TRUE
|
||||
@@ -0,0 +1,65 @@
|
||||
//gang.dm
|
||||
//Gang War Game Mode
|
||||
GLOBAL_LIST_INIT(possible_gangs, subtypesof(/datum/team/gang))
|
||||
GLOBAL_LIST_EMPTY(gangs)
|
||||
/datum/game_mode/gang
|
||||
name = "gang war"
|
||||
config_tag = "gang"
|
||||
antag_flag = ROLE_GANG
|
||||
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security")
|
||||
required_players = 15
|
||||
required_enemies = 0
|
||||
recommended_enemies = 2
|
||||
enemy_minimum_age = 14
|
||||
|
||||
announce_span = "danger"
|
||||
announce_text = "A violent turf war has erupted on the station!\n\
|
||||
<span class='danger'>Gangsters</span>: Take over the station with a dominator.\n\
|
||||
<span class='notice'>Crew</span>: Prevent the gangs from expanding and initiating takeover."
|
||||
|
||||
var/list/datum/mind/gangboss_candidates = list()
|
||||
|
||||
/datum/game_mode/gang/generate_report()
|
||||
return "Cybersun Industries representatives claimed that they, in joint research with the Tiger Cooperative, have made a major breakthrough in brainwashing technology, and have \
|
||||
made the nanobots that apply the \"conversion\" very small and capable of fitting into usually innocent objects - namely, pens. While they refused to outsource this technology for \
|
||||
months to come due to its flaws, they reported some as missing but passed it off to carelessness. At Central Command, we don't like mysteries, and we have reason to believe that this \
|
||||
technology was stolen for anti-Nanotrasen use. Be on the lookout for territory claims and unusually violent crew behavior, applying mindshield implants as necessary."
|
||||
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
//Spawn more bosses depending on server population
|
||||
var/gangs_to_create = 2
|
||||
if(prob(num_players()) && num_players() > 1.5*required_players)
|
||||
gangs_to_create++
|
||||
if(prob(num_players()) && num_players() > 2*required_players)
|
||||
gangs_to_create++
|
||||
gangs_to_create = min(gangs_to_create, GLOB.possible_gangs.len)
|
||||
|
||||
for(var/i in 1 to gangs_to_create)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
|
||||
//Now assign a boss for the gang
|
||||
var/datum/mind/boss = pick_n_take(antag_candidates)
|
||||
antag_candidates -= boss
|
||||
gangboss_candidates += boss
|
||||
boss.restricted_roles = restricted_jobs
|
||||
|
||||
if(gangboss_candidates.len < 1) //Need at least one gangs
|
||||
return
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
set waitfor = FALSE
|
||||
..()
|
||||
for(var/i in gangboss_candidates)
|
||||
var/datum/mind/M = i
|
||||
var/datum/antagonist/gang/boss/B = new()
|
||||
M.add_antag_datum(B)
|
||||
B.equip_gang()
|
||||
@@ -0,0 +1,259 @@
|
||||
//gangtool device
|
||||
/obj/item/device/gangtool
|
||||
name = "suspicious device"
|
||||
desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "gangtool"
|
||||
item_state = "radio"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
flags_1 = CONDUCT_1
|
||||
var/datum/team/gang/gang //Which gang uses this?
|
||||
var/recalling = 0
|
||||
var/outfits = 2
|
||||
var/free_pen = 0
|
||||
var/promotable = FALSE
|
||||
var/static/list/buyable_items = list()
|
||||
var/list/tags = list()
|
||||
|
||||
/obj/item/device/gangtool/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
for(var/i in subtypesof(/datum/gang_item))
|
||||
var/datum/gang_item/G = i
|
||||
var/id = initial(G.id)
|
||||
var/cat = initial(G.category)
|
||||
if(id)
|
||||
if(!islist(buyable_items[cat]))
|
||||
buyable_items[cat] = list()
|
||||
buyable_items[cat][id] = new G
|
||||
/obj/item/device/gangtool/Destroy()
|
||||
if(gang)
|
||||
gang.gangtools -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/device/gangtool/attack_self(mob/user)
|
||||
..()
|
||||
if (!can_use(user))
|
||||
return
|
||||
var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/dat
|
||||
if(!gang)
|
||||
dat += "This device is not registered.<br><br>"
|
||||
if(L)
|
||||
if(promotable && L.gang.leaders.len < L.gang.max_leaders)
|
||||
dat += "Give this device to another member of your organization to use to promote them to Lieutenant.<br><br>"
|
||||
dat += "If this is meant as a spare device for yourself:<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Register Device as Spare</a><br>"
|
||||
else if(promotable)
|
||||
var/datum/antagonist/gang/sweet = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(sweet.gang.leaders.len < sweet.gang.max_leaders)
|
||||
dat += "You have been selected for a promotion!<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Accept Promotion</a><br>"
|
||||
else
|
||||
dat += "No promotions available: All positions filled.<br>"
|
||||
else
|
||||
dat += "This device is not authorized to promote.<br>"
|
||||
else
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
dat += "<center><font color='red'>Takeover In Progress:<br><B>[DisplayTimeText(gang.domination_time_remaining() * 10)] remain</B></font></center>"
|
||||
|
||||
dat += "Registration: <B>[gang.name] Gang Boss</B><br>"
|
||||
dat += "Organization Size: <B>[gang.members.len]</B> | Station Control: <B>[gang.territories.len] territories under control.</B> | Influence: <B>[gang.influence]</B><br>"
|
||||
dat += "Time until Influence grows: <B>[time2text(gang.next_point_time - world.time, "mm:ss")]</B><br>"
|
||||
dat += "<a href='?src=[REF(src)];commute=1'>Send message to Gang</a><br>"
|
||||
dat += "<a href='?src=[REF(src)];recall=1'>Recall shuttle</a><br>"
|
||||
dat += "<hr>"
|
||||
for(var/cat in buyable_items)
|
||||
dat += "<b>[cat]</b><br>"
|
||||
for(var/id in buyable_items[cat])
|
||||
var/datum/gang_item/G = buyable_items[cat][id]
|
||||
if(!G.can_see(user, gang, src))
|
||||
continue
|
||||
|
||||
var/cost = G.get_cost_display(user, gang, src)
|
||||
if(cost)
|
||||
dat += cost + " "
|
||||
|
||||
var/toAdd = G.get_name_display(user, gang, src)
|
||||
if(G.can_buy(user, gang, src))
|
||||
toAdd = "<a href='?src=[REF(src)];purchase=1;id=[id];cat=[cat]'>[toAdd]</a>"
|
||||
dat += toAdd
|
||||
var/extra = G.get_extra_info(user, gang, src)
|
||||
if(extra)
|
||||
dat += "<br><i>[extra]</i>"
|
||||
dat += "<br>"
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<a href='?src=[REF(src)];choice=refresh'>Refresh</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v4.0", 340, 625)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/item/device/gangtool/Topic(href, href_list)
|
||||
if(!can_use(usr))
|
||||
return
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
if(href_list["register"])
|
||||
register_device(usr)
|
||||
|
||||
else if(!gang) //Gangtool must be registered before you can use the functions below
|
||||
return
|
||||
|
||||
if(href_list["purchase"])
|
||||
if(islist(buyable_items[href_list["cat"]]))
|
||||
var/list/L = buyable_items[href_list["cat"]]
|
||||
var/datum/gang_item/G = L[href_list["id"]]
|
||||
if(G && G.can_buy(usr, gang, src))
|
||||
G.purchase(usr, gang, src, FALSE)
|
||||
|
||||
if(href_list["commute"])
|
||||
ping_gang(usr)
|
||||
if(href_list["recall"])
|
||||
recall(usr)
|
||||
attack_self(usr)
|
||||
|
||||
/obj/item/device/gangtool/update_icon()
|
||||
overlays.Cut()
|
||||
var/image/I = new(icon, "[icon_state]-overlay")
|
||||
if(gang)
|
||||
I.color = gang.color
|
||||
overlays.Add(I)
|
||||
|
||||
/obj/item/device/gangtool/proc/ping_gang(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message")
|
||||
if(!message || !can_use(user))
|
||||
return
|
||||
if(!is_station_level(user.z))
|
||||
to_chat(user, "<span class='info'>[icon2html(src, user)]Error: Station out of range.</span>")
|
||||
return
|
||||
if(gang.members.len)
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
return
|
||||
var/ping = "<span class='danger'><B><i>[gang.name] [G.message_name] [user.real_name]</i>: [message]</B></span>"
|
||||
for(var/datum/mind/ganger in gang.members)
|
||||
if(ganger.current && is_station_level(ganger.current.z) && (ganger.current.stat == CONSCIOUS))
|
||||
to_chat(ganger.current, ping)
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
var/link = FOLLOW_LINK(M, user)
|
||||
to_chat(M, "[link] [ping]")
|
||||
user.log_talk(message,LOG_SAY, tag="[gang.name] gangster")
|
||||
|
||||
/obj/item/device/gangtool/proc/register_device(mob/user)
|
||||
if(gang) //It's already been registered!
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G)
|
||||
gang = G.gang
|
||||
gang.gangtools += src
|
||||
update_icon()
|
||||
if(!(user.mind in gang.leaders) && promotable)
|
||||
G.promote()
|
||||
free_pen = TRUE
|
||||
gang.message_gangtools("[user] has been promoted to Lieutenant.")
|
||||
to_chat(user, "The <b>Gangtool</b> you registered will allow you to purchase weapons and equipment, and send messages to your gang.")
|
||||
to_chat(user, "Unlike regular gangsters, you may use <b>recruitment pens</b> to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool.")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>ACCESS DENIED: Unauthorized user.</span>")
|
||||
|
||||
/obj/item/device/gangtool/proc/recall(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
if(recalling)
|
||||
to_chat(user, "<span class='warning'>Error: Recall already in progress.</span>")
|
||||
return
|
||||
gang.message_gangtools("[user] is attempting to recall the emergency shuttle.")
|
||||
recalling = TRUE
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Generating shuttle recall order with codes retrieved from last call signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall2, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall2(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Shuttle recall order generated. Accessing station long-range communication arrays...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall3, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall3(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
var/list/living_crew = list()//shamelessly copied from mulligan code, there should be a helper for this
|
||||
for(var/mob/Player in GLOB.mob_list)
|
||||
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client)
|
||||
living_crew += Player
|
||||
var/malc = CONFIG_GET(number/midround_antag_life_check)
|
||||
if(living_crew.len / GLOB.joined_player_list.len <= malc) //Shuttle cannot be recalled if too many people died
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Station communication systems compromised. Unable to establish connection.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Comm arrays accessed. Broadcasting recall signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recallfinal, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recallfinal(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
recalling = FALSE
|
||||
log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.")
|
||||
message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1)
|
||||
if(SSshuttle.cancelEvac(user))
|
||||
gang.recalls--
|
||||
return TRUE
|
||||
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]No response recieved. Emergency shuttle cannot be recalled at this time.</span>")
|
||||
return
|
||||
|
||||
/obj/item/device/gangtool/proc/recallchecks(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
if(SSshuttle.emergencyNoRecall)
|
||||
return
|
||||
if(!gang.recalls)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Emergency shuttle cannot be recalled at this time.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!gang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!is_station_level(user.z)) //Shuttle can only be recalled while on station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Device out of range of station communication arrays.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.incapacitated())
|
||||
return
|
||||
if(!(src in user.contents))
|
||||
return
|
||||
if(!user.mind)
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
to_chat(user, "<span class='notice'>Huh, what's this?</span>")
|
||||
return
|
||||
if(!isnull(gang) && G.gang != gang)
|
||||
to_chat(user, "<span class='danger'>You cannot use gang tools owned by enemy gangs!</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/device/gangtool/spare
|
||||
outfits = TRUE
|
||||
|
||||
/obj/item/device/gangtool/spare/lt
|
||||
promotable = TRUE
|
||||
@@ -0,0 +1,61 @@
|
||||
/obj/item/implant/gang
|
||||
name = "gang implant"
|
||||
desc = "Makes you a gangster or such."
|
||||
activated = 0
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/item/implant/gang/Initialize(loc, setgang)
|
||||
.=..()
|
||||
gang = setgang
|
||||
|
||||
/obj/item/implant/gang/Destroy()
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/gang/get_data()
|
||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||
<b>Name:</b> Criminal brainwash implant<BR>
|
||||
<b>Life:</b> A few seconds after injection.<BR>
|
||||
<b>Important Notes:</b> Illegal<BR>
|
||||
<HR>
|
||||
<b>Implant Details:</b><BR>
|
||||
<b>Function:</b> Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.<BR>
|
||||
<b>Special Features:</b> This device will also emit a small EMP pulse, destroying any other implants within the host's brain.<BR>
|
||||
<b>Integrity:</b> Implant's EMP function will destroy itself in the process."}
|
||||
return dat
|
||||
|
||||
/obj/item/implant/gang/implant(mob/living/target, mob/user, silent = 0)
|
||||
if(!target || !target.mind || target.stat == DEAD)
|
||||
return 0
|
||||
var/datum/antagonist/gang/G = target.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G && G.gang == G)
|
||||
return 0 // it's pointless
|
||||
if(..())
|
||||
for(var/obj/item/implant/I in target.implants)
|
||||
if(I != src)
|
||||
qdel(I)
|
||||
|
||||
if(ishuman(target))
|
||||
var/success
|
||||
if(G)
|
||||
if(!istype(G, /datum/antagonist/gang/boss))
|
||||
success = TRUE //Was not a gang boss, convert as usual
|
||||
target.mind.remove_antag_datum(/datum/antagonist/gang)
|
||||
else
|
||||
success = TRUE
|
||||
if(!success)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel the influence of your enemies try to invade your mind!</span>")
|
||||
return FALSE
|
||||
target.mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
/obj/item/implanter/gang
|
||||
name = "implanter (gang)"
|
||||
|
||||
/obj/item/implanter/gang/Initialize(loc, gang)
|
||||
if(!gang)
|
||||
qdel(src)
|
||||
return
|
||||
imp = new /obj/item/implant/gang(src,gang)
|
||||
.=..()
|
||||
@@ -14,8 +14,8 @@
|
||||
false_report_weight = 10
|
||||
restricted_jobs = list("AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_players = 30
|
||||
required_enemies = 2
|
||||
required_players = 20
|
||||
required_enemies = 1
|
||||
recommended_enemies = 3
|
||||
enemy_minimum_age = 14
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
// update the invisibility and icon
|
||||
/obj/machinery/bluespace_beacon/hide(intact)
|
||||
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
// update the icon_state
|
||||
/obj/machinery/bluespace_beacon/proc/updateicon()
|
||||
/obj/machinery/bluespace_beacon/update_icon()
|
||||
var/state="floor_beacon"
|
||||
|
||||
if(invisibility)
|
||||
@@ -45,4 +45,4 @@
|
||||
else if (Beacon.loc != loc)
|
||||
Beacon.forceMove(loc)
|
||||
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
@@ -36,12 +36,11 @@
|
||||
RefreshParts()
|
||||
add_inital_chems()
|
||||
|
||||
/obj/machinery/sleeper/Destroy()
|
||||
var/obj/item/reagent_containers/sleeper_buffer/buffer = new /obj/item/reagent_containers/sleeper_buffer(loc)
|
||||
/obj/machinery/sleeper/on_deconstruction()
|
||||
var/obj/item/reagent_containers/sleeper_buffer/buffer = new (loc)
|
||||
buffer.volume = reagents.maximum_volume
|
||||
buffer.reagents.maximum_volume = reagents.maximum_volume
|
||||
reagents.trans_to(buffer.reagents, reagents.total_volume)
|
||||
..()
|
||||
|
||||
/obj/machinery/sleeper/proc/add_inital_chems()
|
||||
for(var/i in available_chems)
|
||||
@@ -182,12 +181,14 @@
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/sleeper/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!user.canUseTopic(src, !issilicon(user)))
|
||||
return
|
||||
if(state_open)
|
||||
close_machine()
|
||||
else
|
||||
open_machine()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/sleeper/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -181,8 +181,9 @@ Class Procs:
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
L.update_canmove()
|
||||
SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
|
||||
occupant = null
|
||||
if(occupant)
|
||||
SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
|
||||
occupant = null
|
||||
|
||||
/obj/machinery/proc/can_be_occupant(atom/movable/am)
|
||||
return occupant_typecache ? is_type_in_typecache(am, occupant_typecache) : isliving(am)
|
||||
|
||||
@@ -132,8 +132,8 @@
|
||||
..()
|
||||
if(!user.canUseTopic(src))
|
||||
return
|
||||
else
|
||||
eject_part(user)
|
||||
eject_part(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/aug_manipulator/power_change()
|
||||
..()
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
var/filling = FALSE
|
||||
var/obj/item/reagent_containers/blood/bag = null
|
||||
var/obj/item/reagent_containers/blood/outbag = null
|
||||
var/bloodstored = 0
|
||||
var/maxbloodstored = 1000
|
||||
var/menustat = "menu"
|
||||
var/efficiency = 0
|
||||
@@ -20,7 +19,7 @@
|
||||
|
||||
/obj/machinery/bloodbankgen/Initialize()
|
||||
. = ..()
|
||||
create_reagents(1000)
|
||||
create_reagents(maxbloodstored, AMOUNT_VISIBLE)
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/bloodbankgen/Destroy()
|
||||
@@ -28,12 +27,13 @@
|
||||
QDEL_NULL(outbag)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/bloodbankgen/contents_explosion(severity, target)
|
||||
..()
|
||||
/obj/machinery/bloodbankgen/examine(mob/user)
|
||||
. = ..()
|
||||
if(bag)
|
||||
bag.ex_act(severity, target)
|
||||
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>input</b> slot. The counter reads: \"Current Capacity: [bag.reagents.total_volume] of [bag.reagents.maximum_volume]\"</span>"
|
||||
if(outbag)
|
||||
outbag.ex_act(severity, target)
|
||||
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>output</b> slot. The counter reads: \"Current Capacity: [outbag.reagents.total_volume] of [outbag.reagents.maximum_volume]\"</span>"
|
||||
|
||||
|
||||
/obj/machinery/bloodbankgen/handle_atom_del(atom/A)
|
||||
..()
|
||||
@@ -115,80 +115,48 @@
|
||||
|
||||
/obj/machinery/bloodbankgen/process()
|
||||
if(!is_operational())
|
||||
return PROCESS_KILL
|
||||
|
||||
bloodstored = reagents.total_volume
|
||||
return
|
||||
|
||||
var/transfer_amount = 20
|
||||
|
||||
if(draining)
|
||||
if(reagents.total_volume >= reagents.maximum_volume)
|
||||
draining = FALSE
|
||||
if(reagents.total_volume >= reagents.maximum_volume || !bag || !bag.reagents.total_volume)
|
||||
beep_stop_pumping()
|
||||
return
|
||||
|
||||
if(bag)
|
||||
if(bag.reagents.total_volume)
|
||||
var/datum/reagent/blood/B = bag.reagents.has_reagent("blood")
|
||||
if(B)
|
||||
var/amount = reagents.maximum_volume - reagents.total_volume //monitor the machine's internal storage
|
||||
amount = min(amount, transfer_amount)
|
||||
if(!amount)
|
||||
draining = FALSE
|
||||
updateUsrDialog()
|
||||
visible_message("[src] beeps loudly.")
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
return
|
||||
|
||||
if(bag.blood_type == "SY") //no infinite loops using synthetics.
|
||||
reagents.add_reagent("syntheticblood", amount)
|
||||
else
|
||||
reagents.add_reagent("syntheticblood", (amount+(5*efficiency)))
|
||||
|
||||
if(bag.reagents.total_volume >= amount)
|
||||
bag.reagents.remove_reagent("blood", amount)
|
||||
else
|
||||
visible_message("[src] beeps loudly.")
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
draining = FALSE
|
||||
|
||||
bag.update_icon()
|
||||
update_icon()
|
||||
updateUsrDialog()
|
||||
else
|
||||
draining = FALSE
|
||||
updateUsrDialog()
|
||||
var/blood_amount = bag.reagents.get_reagent_amount("blood")
|
||||
//monitor the machine and blood bag's reagents storage.
|
||||
var/amount = min(blood_amount, min(transfer_amount, reagents.maximum_volume - reagents.total_volume))
|
||||
if(!amount)
|
||||
beep_stop_pumping()
|
||||
return
|
||||
var/bonus = bag.blood_type == "SY" ? 0 : 5 * efficiency //no infinite loops using synthetics.
|
||||
reagents.add_reagent("syntheticblood", amount + bonus)
|
||||
bag.reagents.remove_reagent("blood", amount)
|
||||
update_icon()
|
||||
|
||||
if(filling)
|
||||
if(!reagents || !reagents.total_volume)
|
||||
filling = FALSE //there ain't anything in the machine yo.
|
||||
if(!reagents.total_volume || !outbag || outbag.reagents.total_volume >= outbag.reagents.maximum_volume)
|
||||
beep_stop_pumping("[src] pings.", TRUE)
|
||||
return
|
||||
if(outbag && outbag.reagents.total_volume < outbag.reagents.maximum_volume)
|
||||
var/amount = outbag.reagents.maximum_volume - outbag.reagents.total_volume //monitor the output bag's internal storage
|
||||
amount = min(amount, transfer_amount)
|
||||
if(!amount)
|
||||
filling = FALSE
|
||||
visible_message("[src] pings.")
|
||||
playsound(loc, 'sound/machines/beep.ogg', 50, 1)
|
||||
updateUsrDialog()
|
||||
return
|
||||
//monitor the output bag's reagents storage.
|
||||
var/amount = min(transfer_amount, outbag.reagents.maximum_volume - outbag.reagents.total_volume)
|
||||
reagents.trans_to(outbag, amount)
|
||||
update_icon()
|
||||
|
||||
reagents.trans_to(outbag, amount)
|
||||
outbag.update_icon()
|
||||
update_icon()
|
||||
updateUsrDialog()
|
||||
else
|
||||
visible_message("[src] pings.")
|
||||
playsound(loc, 'sound/machines/beep.ogg', 50, 1)
|
||||
filling = FALSE
|
||||
updateUsrDialog()
|
||||
return
|
||||
/obj/machinery/bloodbankgen/proc/beep_stop_pumping(msg = "[src] beeps loudly.", out_instead_of_in = FALSE)
|
||||
if(out_instead_of_in)
|
||||
filling = FALSE
|
||||
else
|
||||
draining = FALSE
|
||||
updateUsrDialog()
|
||||
audible_message(msg)
|
||||
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
|
||||
/obj/machinery/bloodbankgen/attackby(obj/item/O, mob/user, params)
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
return ..()
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "bloodbank-off", "bloodbank-off", O))
|
||||
if(default_deconstruction_screwdriver(user, "bloodbank-off", "bloodbank-off", O) || default_unfasten_wrench(user, O, 20) == SUCCESSFUL_UNFASTEN)
|
||||
if(bag)
|
||||
var/obj/item/reagent_containers/blood/B = bag
|
||||
B.forceMove(drop_location())
|
||||
@@ -204,31 +172,37 @@
|
||||
return
|
||||
|
||||
if(istype(O, /obj/item/reagent_containers/blood))
|
||||
. = 1 //no afterattack
|
||||
if(!panel_open)
|
||||
if(bag && outbag)
|
||||
to_chat(user, "<span class='warning'>This machine already has bags attached.</span>")
|
||||
|
||||
if(!bag && !outbag)
|
||||
var/choice = alert(user, "Choose where to place [O]", "", "Input", "Cancel", "Output")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
if("Input")
|
||||
attachinput(O, user)
|
||||
if("Output")
|
||||
attachoutput(O, user)
|
||||
else if(!bag)
|
||||
attachinput(O, user)
|
||||
else if(!outbag)
|
||||
attachoutput(O, user)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Close the maintenance panel first.</span>")
|
||||
return
|
||||
. = TRUE //no afterattack
|
||||
var/msg
|
||||
if(panel_open)
|
||||
. += "Close the maintenance panel"
|
||||
if(!anchored)
|
||||
. += "[msg ? " and a" : "A"]nchor its bolts"
|
||||
if(length(msg))
|
||||
to_chat(user, "<span class='warning'>[msg] first.</span>")
|
||||
return
|
||||
if(bag && outbag)
|
||||
to_chat(user, "<span class='warning'>This machine already has bags attached.</span>")
|
||||
|
||||
if(!bag && !outbag)
|
||||
var/choice = alert(user, "Choose where to place [O]", "", "Input", "Cancel", "Output")
|
||||
switch(choice)
|
||||
if("Cancel")
|
||||
return FALSE
|
||||
if("Input")
|
||||
attachinput(O, user)
|
||||
if("Output")
|
||||
attachoutput(O, user)
|
||||
else if(!bag)
|
||||
attachinput(O, user)
|
||||
else if(!outbag)
|
||||
attachoutput(O, user)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You cannot put this in [src]!</span>")
|
||||
|
||||
/obj/machinery/bloodbankgen/is_operational()
|
||||
return ..() && anchored
|
||||
|
||||
/obj/machinery/bloodbankgen/ui_interact(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -268,7 +242,7 @@
|
||||
if(!bag && !outbag)
|
||||
dat += "<div class='statusDisplay'>No containers inside, please insert container.</div>"
|
||||
|
||||
var/datum/browser/popup = new(user, "bloodbankgen", name, 350, 520)
|
||||
var/datum/browser/popup = new(user, "bloodbankgen", name, 350, 420)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
@@ -306,6 +280,7 @@
|
||||
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
|
||||
usr.put_in_hands(bag)
|
||||
bag = null
|
||||
draining = null
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/bloodbankgen/proc/detachoutput()
|
||||
@@ -314,6 +289,7 @@
|
||||
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
|
||||
usr.put_in_hands(outbag)
|
||||
outbag = null
|
||||
filling = null
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/bloodbankgen/proc/attachinput(obj/item/O, mob/user)
|
||||
@@ -339,23 +315,22 @@
|
||||
to_chat(user, "<span class='notice'>There is already something in this slot!</span>")
|
||||
|
||||
/obj/machinery/bloodbankgen/Topic(href, href_list)
|
||||
if(..() || panel_open)
|
||||
. = ..()
|
||||
if(. | !is_operational())
|
||||
return
|
||||
|
||||
usr.set_machine(src)
|
||||
|
||||
if(href_list["activateinput"])
|
||||
activateinput()
|
||||
updateUsrDialog()
|
||||
|
||||
else if(href_list["detachinput"])
|
||||
detachinput()
|
||||
updateUsrDialog()
|
||||
|
||||
else if(href_list["activateoutput"])
|
||||
activateoutput()
|
||||
updateUsrDialog()
|
||||
|
||||
else if(href_list["detachoutput"])
|
||||
detachoutput()
|
||||
updateUsrDialog()
|
||||
|
||||
updateUsrDialog()
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var/chargelevel = -1
|
||||
var/charge_rate = 500
|
||||
|
||||
/obj/machinery/cell_charger/proc/updateicon()
|
||||
/obj/machinery/cell_charger/update_icon()
|
||||
cut_overlays()
|
||||
if(charging)
|
||||
add_overlay(image(charging.icon, charging.icon_state))
|
||||
@@ -53,7 +53,7 @@
|
||||
charging = W
|
||||
user.visible_message("[user] inserts a cell into [src].", "<span class='notice'>You insert a cell into [src].</span>")
|
||||
chargelevel = -1
|
||||
updateicon()
|
||||
update_icon()
|
||||
else
|
||||
if(!charging && default_deconstruction_screwdriver(user, icon_state, icon_state, W))
|
||||
return
|
||||
@@ -76,7 +76,7 @@
|
||||
charging.update_icon()
|
||||
charging = null
|
||||
chargelevel = -1
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/cell_charger/attack_hand(mob/user)
|
||||
. = ..()
|
||||
@@ -127,4 +127,4 @@
|
||||
use_power(charge_rate)
|
||||
charging.give(charge_rate) //this is 2558, efficient batteries exist
|
||||
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
@@ -133,12 +133,13 @@
|
||||
..()
|
||||
|
||||
/obj/structure/frame/computer/AltClick(mob/user)
|
||||
..()
|
||||
. = ..()
|
||||
if(!isliving(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
return
|
||||
|
||||
if(anchored)
|
||||
to_chat(usr, "<span class='warning'>You must unwrench [src] before rotating it!</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
setDir(turn(dir, -90))
|
||||
return TRUE
|
||||
|
||||
@@ -170,19 +170,19 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/card/AltClick(mob/user)
|
||||
..()
|
||||
. = ..()
|
||||
if(!user.canUseTopic(src, !issilicon(user)) || !is_operational())
|
||||
return
|
||||
if(inserted_modify_id)
|
||||
if(id_eject(user, inserted_modify_id))
|
||||
inserted_modify_id = null
|
||||
updateUsrDialog()
|
||||
return
|
||||
return TRUE
|
||||
if(inserted_scan_id)
|
||||
if(id_eject(user, inserted_scan_id))
|
||||
inserted_scan_id = null
|
||||
updateUsrDialog()
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/card/ui_interact(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -42,12 +42,6 @@
|
||||
. = ..()
|
||||
GLOB.shuttle_caller_list += src
|
||||
|
||||
/obj/machinery/computer/communications/process()
|
||||
if(..())
|
||||
var/ai_autoupdate = aistate != STATE_STATUSDISPLAY && aistate != STATE_CALLSHUTTLE && aistate != STATE_PURCHASE && aistate != STATE_VIEWMESSAGE
|
||||
var/machine_user_autoupdate = state != STATE_STATUSDISPLAY && state != STATE_CALLSHUTTLE && state != STATE_PURCHASE && state != STATE_VIEWMESSAGE
|
||||
updateDialog(machine_user_autoupdate,ai_autoupdate)
|
||||
|
||||
/obj/machinery/computer/communications/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
@@ -76,7 +70,7 @@
|
||||
if(check_access(I))
|
||||
authenticated = 1
|
||||
auth_id = "[I.registered_name] ([I.assignment])"
|
||||
if((20 in I.access))
|
||||
if((ACCESS_CAPTAIN in I.access))
|
||||
authenticated = 2
|
||||
playsound(src, 'sound/machines/terminal_on.ogg', 50, 0)
|
||||
if(obj_flags & EMAGGED)
|
||||
@@ -285,7 +279,7 @@
|
||||
|
||||
// OMG CENTCOM LETTERHEAD
|
||||
if("MessageCentCom")
|
||||
if(authenticated==2)
|
||||
if(authenticated)
|
||||
if(!checkCCcooldown())
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
return
|
||||
@@ -586,8 +580,6 @@
|
||||
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/communications/proc/get_javascript_header(form_id)
|
||||
var/dat = {"<script type="text/javascript">
|
||||
|
||||
@@ -74,8 +74,8 @@
|
||||
t += "<A href='?src=[REF(src)];lowery=1;raisex=1;pad=[current_pad]'>O</A><BR>"//down-right
|
||||
t += "<BR>"
|
||||
t += "<div class='statusDisplay'>Current offset:</div><BR>"
|
||||
t += "<div class='statusDisplay'>[abs(pad.y_offset)] [pad.y_offset > 0 ? "N":"S"]</div><BR>"
|
||||
t += "<div class='statusDisplay'>[abs(pad.x_offset)] [pad.x_offset > 0 ? "E":"W"]</div><BR>"
|
||||
t += "<div class='statusDisplay'>[abs(pad.y_offset)] [pad.y_offset > 0 ? "N":"S"] <a href='?src=[REF(src)];sety=1;pad=[current_pad]'>\[SET\]</a></div><BR>"
|
||||
t += "<div class='statusDisplay'>[abs(pad.x_offset)] [pad.x_offset > 0 ? "E":"W"] <a href='?src=[REF(src)];setx=1;pad=[current_pad]'>\[SET\]</a></div><BR>"
|
||||
|
||||
t += "<BR><A href='?src=[REF(src)];launch=1;pad=[current_pad]'>Launch</A>"
|
||||
t += " <A href='?src=[REF(src)];pull=1;pad=[current_pad]'>Pull</A>"
|
||||
@@ -132,6 +132,16 @@
|
||||
if(!new_name)
|
||||
return
|
||||
pad.display_name = new_name
|
||||
|
||||
if(href_list["setx"])
|
||||
var/newx = input(usr, "Input new x offset", pad.display_name, pad.x_offset) as null|num
|
||||
if(!isnull(newx))
|
||||
pad.x_offset = CLAMP(newx, -pad.range, pad.range)
|
||||
|
||||
if(href_list["sety"])
|
||||
var/newy = input(usr, "Input new y offset", pad.display_name, pad.y_offset) as null|num
|
||||
if(!isnull(newy))
|
||||
pad.y_offset = CLAMP(newy, -pad.range, pad.range)
|
||||
|
||||
if(href_list["remove"])
|
||||
if(usr && alert(usr, "Are you sure?", "Remove Launchpad", "I'm Sure", "Abort") != "Abort")
|
||||
@@ -145,4 +155,4 @@
|
||||
sending = FALSE
|
||||
teleport(usr, pad)
|
||||
|
||||
updateDialog()
|
||||
updateDialog()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
if(contained_id)
|
||||
contained_id.forceMove(get_turf(src))
|
||||
return ..()
|
||||
|
||||
|
||||
|
||||
/obj/machinery/computer/prisoner/examine(mob/user)
|
||||
. = ..()
|
||||
@@ -15,8 +15,9 @@
|
||||
|
||||
|
||||
/obj/machinery/computer/prisoner/AltClick(mob/user)
|
||||
..()
|
||||
id_eject(user)
|
||||
return ..()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/prisoner/P)
|
||||
if(istype(P))
|
||||
|
||||
@@ -115,8 +115,10 @@
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/defibrillator_mount/AltClick(mob/living/carbon/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
. = TRUE
|
||||
if(!defib)
|
||||
to_chat(user, "<span class='warning'>It'd be hard to remove a defib unit from a mount that has none.</span>")
|
||||
return
|
||||
|
||||
@@ -153,9 +153,11 @@
|
||||
. += "<span class='notice'>Alt-click to toggle modes.</span>"
|
||||
|
||||
/obj/item/grenade/barrier/AltClick(mob/living/carbon/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
toggle_mode(user)
|
||||
return TRUE
|
||||
|
||||
/obj/item/grenade/barrier/proc/toggle_mode(mob/user)
|
||||
switch(mode)
|
||||
|
||||
@@ -97,8 +97,10 @@
|
||||
do_the_dishes(TRUE)
|
||||
|
||||
/obj/machinery/dish_drive/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(user.canUseTopic(src, !issilicon(user)))
|
||||
do_the_dishes(TRUE)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/dish_drive/proc/do_the_dishes(manual)
|
||||
if(!contents.len)
|
||||
|
||||
@@ -164,9 +164,11 @@
|
||||
toggle_open(user)
|
||||
|
||||
/obj/machinery/dna_scannernew/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!user.canUseTopic(src, !issilicon(user)))
|
||||
return
|
||||
interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/dna_scannernew/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
if(voice_activated)
|
||||
flags_1 |= HEAR_1
|
||||
|
||||
/obj/machinery/door/password/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
/obj/machinery/door/password/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(!density || !voice_activated || radio_freq)
|
||||
return
|
||||
|
||||
@@ -58,8 +58,6 @@
|
||||
/obj/machinery/firealarm/update_icon()
|
||||
cut_overlays()
|
||||
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
|
||||
var/area/A = src.loc
|
||||
A = A.loc
|
||||
|
||||
if(panel_open)
|
||||
icon_state = "fire_b[buildstage]"
|
||||
@@ -69,23 +67,32 @@
|
||||
icon_state = "firex"
|
||||
return
|
||||
|
||||
icon_state = "fire0"
|
||||
|
||||
if(stat & NOPOWER)
|
||||
icon_state = "fire0"
|
||||
return
|
||||
|
||||
if(is_station_level(z))
|
||||
add_overlay("overlay_[GLOB.security_level]")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
else
|
||||
add_overlay("overlay_[SEC_LEVEL_GREEN]")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
add_overlay("fire_overlay")
|
||||
|
||||
if(detecting)
|
||||
add_overlay("overlay_[A.fire ? "fire" : "clear"]")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[A.fire ? "fire" : "clear"]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
if(is_station_level(z))
|
||||
add_overlay("fire_[GLOB.security_level]")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
else
|
||||
add_overlay("overlay_fire")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "overlay_fire", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
add_overlay("fire_[SEC_LEVEL_GREEN]")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
|
||||
var/area/A = src.loc
|
||||
A = A.loc
|
||||
|
||||
if(!detecting || !A.fire)
|
||||
add_overlay("fire_off")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
else if(obj_flags & EMAGGED)
|
||||
add_overlay("fire_emagged")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
else
|
||||
add_overlay("fire_on")
|
||||
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
|
||||
|
||||
/obj/machinery/firealarm/emp_act(severity)
|
||||
. = ..()
|
||||
@@ -101,6 +108,7 @@
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
obj_flags |= EMAGGED
|
||||
update_icon()
|
||||
if(user)
|
||||
user.visible_message("<span class='warning'>Sparks fly out of [src]!</span>",
|
||||
"<span class='notice'>You emag [src], disabling its thermal sensors.</span>")
|
||||
@@ -112,51 +120,39 @@
|
||||
alarm()
|
||||
..()
|
||||
|
||||
/obj/machinery/firealarm/proc/alarm()
|
||||
if(!is_operational() && (last_alarm+FIREALARM_COOLDOWN < world.time))
|
||||
/obj/machinery/firealarm/proc/alarm(mob/user)
|
||||
if(!is_operational() || (last_alarm+FIREALARM_COOLDOWN > world.time))
|
||||
return
|
||||
last_alarm = world.time
|
||||
var/area/A = get_area(src)
|
||||
A.firealert(src)
|
||||
playsound(src.loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
|
||||
playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
|
||||
if(user)
|
||||
log_game("[user] triggered a fire alarm at [COORD(src)]")
|
||||
|
||||
/obj/machinery/firealarm/proc/reset()
|
||||
/obj/machinery/firealarm/proc/reset(mob/user)
|
||||
if(!is_operational())
|
||||
return
|
||||
var/area/A = get_area(src)
|
||||
A.firereset(src)
|
||||
if(user)
|
||||
log_game("[user] reset a fire alarm at [COORD(src)]")
|
||||
|
||||
/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "firealarm", name, 300, 150, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/firealarm/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["emagged"] = obj_flags & EMAGGED ? 1 : 0
|
||||
|
||||
if(is_station_level(z))
|
||||
data["seclevel"] = get_security_level()
|
||||
else
|
||||
data["seclevel"] = "green"
|
||||
|
||||
/obj/machinery/firealarm/attack_hand(mob/user)
|
||||
if(buildstage != 2)
|
||||
return ..()
|
||||
add_fingerprint(user)
|
||||
var/area/A = get_area(src)
|
||||
data["alarm"] = A.fire
|
||||
if(A.fire)
|
||||
reset(user)
|
||||
else
|
||||
alarm(user)
|
||||
|
||||
return data
|
||||
/obj/machinery/firealarm/attack_ai(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/firealarm/ui_act(action, params)
|
||||
if(..() || buildstage != 2)
|
||||
return
|
||||
switch(action)
|
||||
if("reset")
|
||||
reset()
|
||||
. = TRUE
|
||||
if("alarm")
|
||||
alarm()
|
||||
. = TRUE
|
||||
/obj/machinery/firealarm/attack_robot(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/firealarm/attackby(obj/item/W, mob/user, params)
|
||||
add_fingerprint(user)
|
||||
@@ -200,6 +196,12 @@
|
||||
to_chat(user, "<span class='notice'>You cut the wires from \the [src].</span>")
|
||||
update_icon()
|
||||
return
|
||||
else if(W.force) //hit and turn it on
|
||||
..()
|
||||
var/area/A = get_area(src)
|
||||
if(!A.fire)
|
||||
alarm()
|
||||
return
|
||||
if(1)
|
||||
if(istype(W, /obj/item/stack/cable_coil))
|
||||
var/obj/item/stack/cable_coil/coil = W
|
||||
@@ -322,8 +324,3 @@
|
||||
if (!party_overlay)
|
||||
party_overlay = iconstate2appearance('icons/turf/areas.dmi', "party")
|
||||
A.add_overlay(party_overlay)
|
||||
|
||||
/obj/machinery/firealarm/partyalarm/ui_data(mob/user)
|
||||
. = ..()
|
||||
var/area/A = get_area(src)
|
||||
.["alarm"] = A && A.party
|
||||
|
||||
@@ -51,10 +51,12 @@
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/harvester/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(harvesting || !user || !isliving(user) || state_open)
|
||||
return
|
||||
if(can_harvest())
|
||||
start_harvest()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/harvester/proc/can_harvest()
|
||||
if(!powered(EQUIP) || state_open || !occupant || !iscarbon(occupant))
|
||||
|
||||
@@ -408,7 +408,7 @@ GLOBAL_LIST_EMPTY(network_holopads)
|
||||
|
||||
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
|
||||
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(speaker && LAZYLEN(masters) && !radio_freq)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios.
|
||||
for(var/mob/living/silicon/ai/master in masters)
|
||||
@@ -418,7 +418,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
for(var/I in holo_calls)
|
||||
var/datum/holocall/HC = I
|
||||
if(HC.connected_holopad == src && speaker != HC.hologram)
|
||||
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
|
||||
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, source)
|
||||
|
||||
if(outgoing_call && speaker == outgoing_call.user)
|
||||
outgoing_call.hologram.say(raw_message)
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
name = "light switch ([area.name])"
|
||||
|
||||
on = area.lightswitch
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/light_switch/proc/updateicon()
|
||||
/obj/machinery/light_switch/update_icon()
|
||||
if(stat & NOPOWER)
|
||||
icon_state = "light-p"
|
||||
else
|
||||
@@ -41,11 +41,11 @@
|
||||
on = !on
|
||||
|
||||
area.lightswitch = on
|
||||
area.updateicon()
|
||||
area.update_icon()
|
||||
|
||||
for(var/obj/machinery/light_switch/L in area)
|
||||
L.on = on
|
||||
L.updateicon()
|
||||
L.update_icon()
|
||||
|
||||
area.power_change()
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
else
|
||||
stat |= NOPOWER
|
||||
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/light_switch/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
// update the invisibility and icon
|
||||
/obj/machinery/magnetic_module/hide(intact)
|
||||
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
// update the icon_state
|
||||
/obj/machinery/magnetic_module/proc/updateicon()
|
||||
/obj/machinery/magnetic_module/update_icon()
|
||||
var/state="floor_magnet"
|
||||
var/onstate=""
|
||||
if(!on)
|
||||
@@ -161,7 +161,7 @@
|
||||
else
|
||||
use_power = NO_POWER_USE
|
||||
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the magneting
|
||||
|
||||
@@ -72,10 +72,10 @@
|
||||
// hide the object if turf is intact
|
||||
/obj/machinery/navbeacon/hide(intact)
|
||||
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
// update the icon_state
|
||||
/obj/machinery/navbeacon/proc/updateicon()
|
||||
/obj/machinery/navbeacon/update_icon()
|
||||
var/state="navbeacon[open]"
|
||||
|
||||
if(invisibility)
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "<span class='notice'>You [open ? "open" : "close"] the beacon's cover.</span>")
|
||||
|
||||
updateicon()
|
||||
update_icon()
|
||||
|
||||
else if (istype(I, /obj/item/card/id)||istype(I, /obj/item/pda))
|
||||
if(open)
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
var/item_recycle_sound = 'sound/items/welder.ogg'
|
||||
|
||||
/obj/machinery/recycler/Initialize()
|
||||
AddComponent(/datum/component/butchering/recycler, 1, amount_produced,amount_produced/5)
|
||||
AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), INFINITY, FALSE, null, null, null, TRUE)
|
||||
AddComponent(/datum/component/butchering, 1, amount_produced,amount_produced/5)
|
||||
. = ..()
|
||||
update_icon()
|
||||
req_one_access = get_all_accesses() + get_all_centcom_access()
|
||||
@@ -35,7 +35,7 @@
|
||||
var/datum/component/material_container/materials = GetComponent(/datum/component/material_container)
|
||||
materials.max_amount = mat_mod
|
||||
amount_produced = min(50, amt_made) + 50
|
||||
var/datum/component/butchering/butchering = GetComponent(/datum/component/butchering)
|
||||
var/datum/component/butchering/butchering = GetComponent(/datum/component/butchering/recycler)
|
||||
butchering.effectiveness = amount_produced
|
||||
butchering.bonus_modifier = amount_produced/5
|
||||
|
||||
@@ -83,20 +83,24 @@
|
||||
is_powered = FALSE
|
||||
icon_state = icon_name + "[is_powered]" + "[(blood ? "bld" : "")]" // add the blood tag at the end
|
||||
|
||||
/obj/machinery/recycler/Bumped(atom/movable/AM)
|
||||
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
/obj/machinery/recycler/CanPass(atom/movable/AM)
|
||||
. = ..()
|
||||
if(!anchored)
|
||||
return
|
||||
if(safety_mode)
|
||||
return
|
||||
|
||||
var/move_dir = get_dir(loc, AM.loc)
|
||||
if(move_dir == eat_dir)
|
||||
eat(AM)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/recycler/Crossed(atom/movable/AM)
|
||||
eat(AM)
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/recycler/proc/eat(atom/AM0, sound=TRUE)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
if(safety_mode)
|
||||
return
|
||||
var/list/to_eat
|
||||
if(isitem(AM0))
|
||||
to_eat = AM0.GetAllContentsIgnoring(GLOB.typecache_mob)
|
||||
@@ -194,9 +198,6 @@
|
||||
// Instantly lie down, also go unconscious from the pain, before you die.
|
||||
L.Unconscious(100)
|
||||
L.adjustBruteLoss(crush_damage)
|
||||
if(L.stat == DEAD && (L.butcher_results || L.guaranteed_butcher_results))
|
||||
var/datum/component/butchering/butchering = GetComponent(/datum/component/butchering)
|
||||
butchering.Butcher(src,L)
|
||||
|
||||
/obj/machinery/recycler/deathtrap
|
||||
name = "dangerous old crusher"
|
||||
|
||||
@@ -326,6 +326,9 @@
|
||||
|
||||
/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params)
|
||||
if(state_open && is_operational())
|
||||
if(istype(I, /obj/item/clothing/head/mob_holder))
|
||||
to_chat(user, "<span class='warning'>You can't quite fit that in while you hold it!</span>")
|
||||
return
|
||||
if(istype(I, /obj/item/clothing/suit))
|
||||
if(suit)
|
||||
to_chat(user, "<span class='warning'>The unit already contains a suit!.</span>")
|
||||
@@ -437,4 +440,4 @@
|
||||
if(I)
|
||||
I.forceMove(loc)
|
||||
. = TRUE
|
||||
update_icon()
|
||||
update_icon()
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
else if(isobj(mobtype))
|
||||
race = "Machinery"
|
||||
|
||||
else if(ispath(mobtype, ))
|
||||
else if(ispath(mobtype, /mob/living/simple_animal))
|
||||
race = "Domestic Animal"
|
||||
|
||||
else
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
))
|
||||
// this will log the signal and transmit it to the target
|
||||
linkedServer.receive_information(signal, null)
|
||||
usr.log_message("(PDA: [name]) sent \"[custommessage]\" to [signal.format_target()]", LOG_PDA)
|
||||
usr.log_message("(PDA: [name] | [usr.real_name]) sent \"[custommessage]\" to [signal.format_target()]", LOG_PDA)
|
||||
|
||||
|
||||
//Request Console Logs - KEY REQUIRED
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
signal.data["slow"] += rand(1, 5) // slow the signal down only slightly
|
||||
|
||||
// Try sending it!
|
||||
var/list/try_send = list(signal.server_type, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster, /obj/machinery/telecomms/bus)
|
||||
var/list/try_send = list(signal.server_type, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster)
|
||||
|
||||
var/i = 0
|
||||
for(var/send in try_send)
|
||||
|
||||
@@ -39,5 +39,5 @@
|
||||
network = "tcommsat"
|
||||
autolinkers = list("hub", "relay", "s_relay", "m_relay", "r_relay", "h_relay", "science", "medical",
|
||||
"supply", "service", "common", "command", "engineering", "security",
|
||||
"receiverA", "receiverB", "broadcasterA", "broadcasterB")
|
||||
"receiverA", "receiverB", "broadcasterA", "broadcasterB", "autorelay")
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
/obj/machinery/telecomms/relay/preset
|
||||
network = "tcommsat"
|
||||
|
||||
/obj/machinery/telecomms/relay/Initialize(mapload)
|
||||
. = ..()
|
||||
if(autolinkers.len) //We want lateloaded presets to autolink (lateloaded aways/ruins/shuttles)
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/machinery/telecomms/relay/preset/station
|
||||
id = "Station Relay"
|
||||
autolinkers = list("s_relay")
|
||||
@@ -74,3 +79,8 @@
|
||||
icon = 'icons/obj/clockwork_objects.dmi'
|
||||
hide = TRUE
|
||||
autolinkers = list("h_relay")
|
||||
|
||||
//Generic preset relay
|
||||
/obj/machinery/telecomms/relay/preset/auto
|
||||
hide = TRUE
|
||||
autolinkers = list("autorelay")
|
||||
|
||||
@@ -107,6 +107,7 @@ GLOBAL_LIST_EMPTY(telecomms_list)
|
||||
for(var/x in autolinkers)
|
||||
if(x in T.autolinkers)
|
||||
links |= T
|
||||
T.links |= src
|
||||
|
||||
/obj/machinery/telecomms/update_icon()
|
||||
if(on)
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
. += "<span class='notice'>Alt-click it to start a wash cycle.</span>"
|
||||
|
||||
/obj/machinery/washing_machine/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!user.canUseTopic(src))
|
||||
return
|
||||
|
||||
@@ -24,11 +25,11 @@
|
||||
|
||||
if(state_open)
|
||||
to_chat(user, "<span class='notice'>Close the door first</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
if(bloody_mess)
|
||||
to_chat(user, "<span class='warning'>[src] must be cleaned up first.</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
if(has_corgi)
|
||||
bloody_mess = 1
|
||||
@@ -37,6 +38,7 @@
|
||||
update_icon()
|
||||
addtimer(CALLBACK(src, .proc/wash_cycle), 200)
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/washing_machine/process()
|
||||
if (!busy)
|
||||
@@ -217,6 +219,10 @@
|
||||
update_icon()
|
||||
return
|
||||
|
||||
if(istype(W, /obj/item/clothing/head/mob_holder))
|
||||
to_chat(user, "<span class='warning'>It's too unwieldly to put in this way.</span>")
|
||||
return 1
|
||||
|
||||
else if(user.a_intent != INTENT_HARM)
|
||||
|
||||
if (!state_open)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
max_temperature = 35000
|
||||
leg_overload_coeff = 100
|
||||
operation_req_access = list(ACCESS_SYNDICATE)
|
||||
internals_req_access = list(ACCESS_SYNDICATE)
|
||||
wreckage = /obj/structure/mecha_wreckage/gygax/dark
|
||||
max_equip = 4
|
||||
spawn_tracked = FALSE
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
max_temperature = 25000
|
||||
infra_luminosity = 5
|
||||
operation_req_access = list(ACCESS_THEATRE)
|
||||
internals_req_access = list(ACCESS_THEATRE, ACCESS_ROBOTICS)
|
||||
wreckage = /obj/structure/mecha_wreckage/honker
|
||||
add_req_access = 0
|
||||
max_equip = 3
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
infra_luminosity = 3
|
||||
operation_req_access = list(ACCESS_CENT_SPECOPS)
|
||||
internals_req_access = list(ACCESS_CENT_SPECOPS, ACCESS_ROBOTICS)
|
||||
wreckage = /obj/structure/mecha_wreckage/marauder
|
||||
add_req_access = 0
|
||||
internal_damage_threshold = 25
|
||||
@@ -46,6 +47,7 @@
|
||||
name = "\improper Seraph"
|
||||
icon_state = "seraph"
|
||||
operation_req_access = list(ACCESS_CENT_SPECOPS)
|
||||
internals_req_access = list(ACCESS_CENT_SPECOPS, ACCESS_ROBOTICS)
|
||||
step_in = 3
|
||||
max_integrity = 550
|
||||
wreckage = /obj/structure/mecha_wreckage/seraph
|
||||
@@ -72,6 +74,7 @@
|
||||
name = "\improper Mauler"
|
||||
icon_state = "mauler"
|
||||
operation_req_access = list(ACCESS_SYNDICATE)
|
||||
internals_req_access = list(ACCESS_SYNDICATE)
|
||||
wreckage = /obj/structure/mecha_wreckage/mauler
|
||||
max_equip = 5
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
layer = ABOVE_MOB_LAYER
|
||||
breach_time = 100 //ten seconds till all goes to shit
|
||||
recharge_rate = 100
|
||||
internals_req_access = list()
|
||||
add_req_access = 0
|
||||
wreckage = /obj/structure/mecha_wreckage/durand/neovgre
|
||||
spawn_tracked = FALSE
|
||||
|
||||
@@ -47,7 +49,7 @@
|
||||
for(var/mob/M in src)
|
||||
to_chat(M, "<span class='brass'>You are consumed by the fires raging within Neovgre...</span>")
|
||||
M.dust()
|
||||
playsound(src, 'sound/magic/lightning_chargeup.ogg', 100, 0)
|
||||
playsound(src, 'sound/effects/neovgre_exploding.ogg', 100, 0)
|
||||
src.visible_message("<span class = 'userdanger'>The reactor has gone critical, its going to blow!</span>")
|
||||
addtimer(CALLBACK(src,.proc/go_critical),breach_time)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
max_temperature = 15000
|
||||
wreckage = /obj/structure/mecha_wreckage/reticence
|
||||
operation_req_access = list(ACCESS_THEATRE)
|
||||
internals_req_access = list(ACCESS_THEATRE, ACCESS_ROBOTICS)
|
||||
add_req_access = 0
|
||||
internal_damage_threshold = 25
|
||||
max_equip = 2
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/drill/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/butchering, 50, 100)
|
||||
AddComponent(/datum/component/butchering, 50, 100, null, null, TRUE)
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/drill/action(atom/target)
|
||||
if(!action_checks(target))
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
var/internal_damage = 0 //contains bitflags
|
||||
|
||||
var/list/operation_req_access = list()//required access level for mecha operation
|
||||
var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//REQUIRED ACCESS LEVEL TO OPEN CELL COMPARTMENT
|
||||
var/list/internals_req_access = list(ACCESS_ROBOTICS)//REQUIRED ACCESS LEVEL TO OPEN CELL COMPARTMENT
|
||||
|
||||
var/wreckage
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
/obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits.
|
||||
return
|
||||
|
||||
/obj/mecha/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
/obj/mecha/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(speaker == occupant)
|
||||
if(radio.broadcasting)
|
||||
|
||||
@@ -138,8 +138,10 @@
|
||||
chassis.toggle_strafe()
|
||||
|
||||
/obj/mecha/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if((user == occupant) && user.canUseTopic(src))
|
||||
toggle_strafe()
|
||||
return TRUE
|
||||
|
||||
/obj/mecha/proc/toggle_strafe()
|
||||
strafe = !strafe
|
||||
|
||||
@@ -72,6 +72,11 @@
|
||||
desc = "It's still good. Four second rule!"
|
||||
icon_state = "flour"
|
||||
|
||||
/obj/effect/decal/cleanable/greenglow/ecto
|
||||
name = "ectoplasmic puddle"
|
||||
desc = "You know who to call."
|
||||
light_power = 2
|
||||
|
||||
/obj/effect/decal/cleanable/greenglow
|
||||
name = "glowing goo"
|
||||
desc = "Jeez. I hope that's not for lunch."
|
||||
|
||||
@@ -234,5 +234,5 @@
|
||||
/obj/effect/turf_decal/trimline/neutral/filled/corner
|
||||
icon_state = "trimline_corner_fill"
|
||||
|
||||
/obj/effect/turf_decal/trimline/brown/filled/end
|
||||
/obj/effect/turf_decal/trimline/neutral/filled/end
|
||||
icon_state = "trimline_end_fill"
|
||||
@@ -165,7 +165,7 @@
|
||||
/obj/item/organ/heart/gland/chem = 5,
|
||||
/obj/item/organ/heart/gland/mindshock = 5,
|
||||
/obj/item/organ/heart/gland/plasma = 7,
|
||||
/obj/item/organ/heart/gland/pop = 5,
|
||||
/obj/item/organ/heart/gland/transform = 5,
|
||||
/obj/item/organ/heart/gland/slime = 4,
|
||||
/obj/item/organ/heart/gland/spiderman = 5,
|
||||
/obj/item/organ/heart/gland/ventcrawling = 1,
|
||||
|
||||
@@ -349,6 +349,10 @@
|
||||
icon_state = "impact_laser_purple"
|
||||
duration = 4
|
||||
|
||||
/obj/effect/temp_visual/impact_effect/shrink
|
||||
icon_state = "m_shield"
|
||||
duration = 10
|
||||
|
||||
/obj/effect/temp_visual/impact_effect/ion
|
||||
icon_state = "shieldsparkles"
|
||||
duration = 6
|
||||
@@ -442,3 +446,10 @@
|
||||
animate(src, alpha = 0, transform = skew, time = duration)
|
||||
else
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/obj/effect/temp_visual/slugboom
|
||||
icon = 'icons/effects/96x96.dmi'
|
||||
icon_state = "slugboom"
|
||||
randomdir = FALSE
|
||||
duration = 30
|
||||
pixel_x = -24
|
||||
@@ -36,3 +36,7 @@
|
||||
|
||||
/obj/effect/projectile/impact/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/impact/laser/wavemotion
|
||||
name = "particle impact"
|
||||
icon_state = "impact_wavemotion"
|
||||
@@ -28,3 +28,7 @@
|
||||
|
||||
/obj/effect/projectile/muzzle/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/muzzle/laser/wavemotion
|
||||
name = "particle backblast"
|
||||
icon_state = "muzzle_wavemotion"
|
||||
@@ -66,3 +66,7 @@
|
||||
|
||||
/obj/effect/projectile/tracer/wormhole
|
||||
icon_state = "wormhole_g"
|
||||
|
||||
/obj/effect/projectile/tracer/laser/wavemotion
|
||||
name = "particle trail"
|
||||
icon_state = "tracer_wavemotion"
|
||||
|
||||
@@ -81,6 +81,7 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
|
||||
var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES
|
||||
var/heat = 0
|
||||
///All items with sharpness of IS_SHARP or higher will automatically get the butchering component.
|
||||
var/sharpness = IS_BLUNT
|
||||
|
||||
var/tool_behaviour = NONE
|
||||
@@ -139,6 +140,9 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
else if (!istype(embedding, /datum/embedding_behavior))
|
||||
stack_trace("Invalid type [embedding.type] found in .embedding during /obj/item Initialize()")
|
||||
|
||||
if(sharpness) //give sharp objects butchering functionality, for consistency
|
||||
AddComponent(/datum/component/butchering, 80 * toolspeed)
|
||||
|
||||
/obj/item/Destroy()
|
||||
item_flags &= ~DROPDEL //prevent reqdels
|
||||
if(ismob(loc))
|
||||
@@ -393,13 +397,14 @@ GLOBAL_VAR_INIT(rpg_loot_items, FALSE)
|
||||
return
|
||||
if(over == src)
|
||||
return usr.client.Click(src, src_location, src_control, params)
|
||||
var/list/directaccess = usr.DirectAccess()
|
||||
var/list/directaccess = usr.DirectAccess() //This, specifically, is what requires the copypaste. If this were after the adjacency check, then it'd be impossible to use items in your inventory, among other things.
|
||||
//If this were before the above checks, then trying to click on items would act a little funky and signal overrides wouldn't work.
|
||||
if((usr.CanReach(src) || (src in directaccess)) && (usr.CanReach(over) || (over in directaccess)))
|
||||
if(!usr.get_active_held_item())
|
||||
usr.UnarmedAttack(src, TRUE)
|
||||
if(usr.get_active_held_item() == src)
|
||||
melee_attack_chain(usr, over)
|
||||
return
|
||||
return TRUE //returning TRUE as a "is this overridden?" flag
|
||||
if(!Adjacent(usr) || !over.Adjacent(usr))
|
||||
return // should stop you from dragging through windows
|
||||
|
||||
|
||||
@@ -133,15 +133,14 @@ RLD
|
||||
if(!(A in range(custom_range, get_turf(user))))
|
||||
to_chat(user, "<span class='warning'>The \'Out of Range\' light on [src] blinks red.</span>")
|
||||
return FALSE
|
||||
else
|
||||
return TRUE
|
||||
|
||||
/obj/item/construction/proc/prox_check(proximity)
|
||||
if(proximity)
|
||||
return TRUE
|
||||
else
|
||||
var/view_range = user.client ? user.client.view : world.view
|
||||
//if user can't be seen from A (only checks surroundings' opaqueness) and can't see A.
|
||||
//jarring, but it should stop people from targetting atoms they can't see...
|
||||
//excluding darkness, to allow RLD to be used to light pitch black dark areas.
|
||||
if(!((user in view(view_range, A)) || (user in viewers(view_range, A))))
|
||||
to_chat(user, "<span class='warning'>You focus, pointing \the [src] at whatever outside your field of vision in the given direction... to no avail.</span>")
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/construction/rcd
|
||||
name = "rapid-construction-device (RCD)"
|
||||
@@ -523,7 +522,12 @@ RLD
|
||||
|
||||
/obj/item/construction/rcd/afterattack(atom/A, mob/user, proximity)
|
||||
. = ..()
|
||||
if(!prox_check(proximity))
|
||||
if(!proximity)
|
||||
if(!ranged || !range_check(A,user)) //early return not-in-range sanity.
|
||||
return
|
||||
if(target_check(A,user))
|
||||
user.Beam(A,icon_state="rped_upgrade",time=30)
|
||||
rcd_create(A,user)
|
||||
return
|
||||
rcd_create(A, user)
|
||||
|
||||
@@ -635,6 +639,7 @@ RLD
|
||||
max_matter = INFINITY
|
||||
matter = INFINITY
|
||||
upgrade = TRUE
|
||||
ranged = TRUE
|
||||
|
||||
// Ranged RCD
|
||||
|
||||
@@ -650,20 +655,10 @@ RLD
|
||||
item_state = "oldrcd"
|
||||
has_ammobar = FALSE
|
||||
|
||||
/obj/item/construction/rcd/arcd/afterattack(atom/A, mob/user)
|
||||
. = ..()
|
||||
if(!range_check(A,user))
|
||||
return
|
||||
if(target_check(A,user))
|
||||
user.Beam(A,icon_state="rped_upgrade",time=30)
|
||||
rcd_create(A,user)
|
||||
|
||||
|
||||
|
||||
// RAPID LIGHTING DEVICE
|
||||
|
||||
|
||||
|
||||
/obj/item/construction/rld
|
||||
name = "rapid-light-device (RLD)"
|
||||
desc = "A device used to rapidly provide lighting sources to an area. Reload with metal, plasteel, glass or compressed matter cartridges."
|
||||
|
||||
@@ -517,16 +517,17 @@ update_label("John Doe", "Clowny")
|
||||
return
|
||||
if(user.incapacitated() || !istype(user))
|
||||
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
|
||||
return
|
||||
return TRUE
|
||||
if(alert("Are you sure you want to recolor your id?", "Confirm Repaint", "Yes", "No") == "Yes")
|
||||
var/energy_color_input = input(usr,"","Choose Energy Color",id_color) as color|null
|
||||
if(!in_range(src, user) || !energy_color_input)
|
||||
return
|
||||
return TRUE
|
||||
if(user.incapacitated() || !istype(user))
|
||||
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
|
||||
return
|
||||
return TRUE
|
||||
id_color = sanitize_hexcolor(energy_color_input, desired_format=6, include_crunch=1)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/card/id/knight/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -665,6 +665,13 @@
|
||||
name = "Booze Dispenser (Machine Board)"
|
||||
build_path = /obj/machinery/chem_dispenser/drinks/beer
|
||||
|
||||
/obj/item/circuitboard/machine/chem_dispenser/abductor
|
||||
name = "Reagent Synthetizer (Abductor Machine Board)"
|
||||
icon_state = "abductor_mod"
|
||||
build_path = /obj/machinery/chem_dispenser/abductor
|
||||
def_components = list(/obj/item/stock_parts/cell = /obj/item/stock_parts/cell/high)
|
||||
needs_anchored = FALSE
|
||||
|
||||
/obj/item/circuitboard/machine/smoke_machine
|
||||
name = "Smoke Machine (Machine Board)"
|
||||
build_path = /obj/machinery/smoke_machine
|
||||
@@ -945,10 +952,12 @@
|
||||
to_chat(user, "<span class='notice'>You [suction ? "enable" : "disable"] the board's suction function.</span>")
|
||||
|
||||
/obj/item/circuitboard/machine/dish_drive/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!user.Adjacent(src))
|
||||
return
|
||||
transmit = !transmit
|
||||
to_chat(user, "<span class='notice'>You [transmit ? "enable" : "disable"] the board's automatic disposal transmission.</span>")
|
||||
return TRUE
|
||||
|
||||
/obj/item/circuitboard/machine/stacking_unit_console
|
||||
name = "Stacking Machine Console (Machine Board)"
|
||||
|
||||
@@ -150,11 +150,13 @@
|
||||
ui.open()
|
||||
|
||||
/obj/item/toy/crayon/spraycan/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
if(has_cap)
|
||||
is_capped = !is_capped
|
||||
to_chat(user, "<span class='notice'>The cap on [src] is now [is_capped ? "on" : "off"].</span>")
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/toy/crayon/proc/staticDrawables()
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
var/pullshocksafely = FALSE //Dose the unit have the healdisk upgrade?
|
||||
var/primetime = 0 // is the defib faster
|
||||
var/timedeath = 10
|
||||
var/disarm_shock_time = 10
|
||||
var/always_emagged = FALSE
|
||||
|
||||
/obj/item/defibrillator/get_cell()
|
||||
return cell
|
||||
@@ -104,7 +106,7 @@
|
||||
|
||||
/obj/item/defibrillator/MouseDrop(obj/over_object)
|
||||
. = ..()
|
||||
if(ismob(loc))
|
||||
if(!. && ismob(loc) && loc == usr)
|
||||
var/mob/M = loc
|
||||
if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
|
||||
var/obj/screen/inventory/hand/H = over_object
|
||||
@@ -140,6 +142,7 @@
|
||||
|
||||
/obj/item/defibrillator/emag_act(mob/user)
|
||||
. = ..()
|
||||
always_emagged = TRUE
|
||||
safety = !safety
|
||||
to_chat(user, "<span class='warning'>You silently [safety ? "enable" : "disable"] [src]'s safety protocols with the cryptographic sequencer.</span>")
|
||||
return TRUE
|
||||
@@ -154,7 +157,7 @@
|
||||
safety = FALSE
|
||||
visible_message("<span class='notice'>[src] beeps: Safety protocols disabled!</span>")
|
||||
playsound(src, 'sound/machines/defib_saftyOff.ogg', 50, 0)
|
||||
else
|
||||
else if(!always_emagged)
|
||||
safety = TRUE
|
||||
visible_message("<span class='notice'>[src] beeps: Safety protocols enabled!</span>")
|
||||
playsound(src, 'sound/machines/defib_saftyOn.ogg', 50, 0)
|
||||
@@ -257,6 +260,8 @@
|
||||
desc = "A belt-equipped blood-red defibrillator that can be rapidly deployed. Does not have the restrictions or safeties of conventional defibrillators and can revive through space suits."
|
||||
combat = TRUE
|
||||
safety = FALSE
|
||||
always_emagged = TRUE
|
||||
disarm_shock_time = 0
|
||||
|
||||
/obj/item/defibrillator/compact/combat/loaded/Initialize()
|
||||
. = ..()
|
||||
@@ -293,6 +298,7 @@
|
||||
var/combat = FALSE //If it penetrates armor and gives additional functionality
|
||||
var/grab_ghost = FALSE
|
||||
var/tlimit = DEFIB_TIME_LIMIT * 10
|
||||
var/disarm_shock_time = 10
|
||||
|
||||
var/mob/listeningTo
|
||||
|
||||
@@ -467,7 +473,7 @@
|
||||
M.visible_message("<span class='danger'>[user] hastily places [src] on [M]'s chest!</span>", \
|
||||
"<span class='userdanger'>[user] hastily places [src] on [M]'s chest!</span>")
|
||||
busy = TRUE
|
||||
if(do_after(user, 10, target = M))
|
||||
if(do_after(user, isnull(defib?.disarm_shock_time)? disarm_shock_time : defib.disarm_shock_time, target = M))
|
||||
M.visible_message("<span class='danger'>[user] zaps [M] with [src]!</span>", \
|
||||
"<span class='userdanger'>[user] zaps [M] with [src]!</span>")
|
||||
M.adjustStaminaLoss(50)
|
||||
@@ -734,9 +740,8 @@
|
||||
/obj/item/disk/medical
|
||||
name = "Defibrillator Upgrade Disk"
|
||||
desc = "A blank upgrade disk, made for a defibrillator"
|
||||
icon = 'modular_citadel/icons/obj/defib_disks.dmi'
|
||||
icon_state = "upgrade_disk"
|
||||
item_state = "heal_disk"
|
||||
icon_state = "heal_disk"
|
||||
item_state = "defib_disk"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
/obj/item/disk/medical/defib_heal
|
||||
@@ -762,5 +767,3 @@
|
||||
desc = "An upgrade to the defibrillator capacitors, which let it charge faster"
|
||||
icon_state = "fast_disk"
|
||||
materials = list(MAT_METAL=16000, MAT_GLASS = 8000, MAT_GOLD = 26000, MAT_SILVER = 26000)
|
||||
|
||||
#undef HALFWAYCRITDEATH
|
||||
|
||||
@@ -828,14 +828,14 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
send_message(U,list(P))
|
||||
|
||||
/obj/item/pda/AltClick()
|
||||
..()
|
||||
|
||||
. = ..()
|
||||
if(id)
|
||||
remove_id()
|
||||
playsound(src, 'sound/machines/terminal_eject_disc.ogg', 50, 1)
|
||||
else
|
||||
remove_pen()
|
||||
playsound(src, 'sound/machines/button4.ogg', 50, 1)
|
||||
return TRUE
|
||||
|
||||
/obj/item/pda/CtrlClick()
|
||||
..()
|
||||
@@ -1044,7 +1044,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
|
||||
if (ismob(loc))
|
||||
var/mob/M = loc
|
||||
M.show_message("<span class='userdanger'>Your [src] explodes!</span>", 1)
|
||||
M.show_message("<span class='userdanger'>Your [src] explodes!</span>", MSG_VISUAL, "<span class='warning'>You hear a loud *pop*!</span>", MSG_AUDIBLE)
|
||||
else
|
||||
visible_message("<span class='danger'>[src] explodes!</span>", "<span class='warning'>You hear a loud *pop*!</span>")
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
rad_flags = RAD_PROTECT_CONTENTS //So the cartridges dont annoyingly get irradiated, and the signallers inside being radded as well
|
||||
|
||||
var/obj/item/integrated_signaler/radio = null
|
||||
|
||||
@@ -689,15 +690,16 @@ Code:
|
||||
active_bot = null
|
||||
|
||||
if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
|
||||
active_bot.bot_control(command= "summon", user_turf= get_turf(usr), user_access= host_pda.GetAccess())
|
||||
active_bot.bot_control("summon", usr, host_pda.GetAccess())
|
||||
|
||||
else //Forward all other bot commands to the bot itself!
|
||||
active_bot.bot_control(command= href_list["op"], user= usr)
|
||||
active_bot.bot_control(href_list["op"], usr)
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if(href_list["mule"]) //MULEbots are special snowflakes, and need different args due to how they work.
|
||||
|
||||
active_bot.bot_control(href_list["mule"], usr, TRUE)
|
||||
var/mob/living/simple_animal/bot/mulebot/mule = active_bot
|
||||
if (istype(mule))
|
||||
active_bot.bot_control(href_list["mule"], usr, TRUE)
|
||||
|
||||
if(!host_pda)
|
||||
return
|
||||
|
||||
@@ -72,12 +72,12 @@
|
||||
difficulty += 2
|
||||
var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink)
|
||||
if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink))
|
||||
U.show_message("<span class='danger'>An error flashes on your [src].</span>", 1)
|
||||
U.show_message("<span class='danger'>An error flashes on your [src].</span>", MSG_VISUAL)
|
||||
else
|
||||
message_admins("[!is_special_character(U) ? "Non-antag " : ""][ADMIN_LOOKUPFLW(U)] triggered a PDA explosion on [target.name] at [ADMIN_VERBOSEJMP(target)].")
|
||||
var/message_log = "triggered a PDA explosion on [target.name] at [AREACOORD(target)]."
|
||||
U.log_message(message_log, LOG_ATTACK)
|
||||
U.show_message("<span class='notice'>Success!</span>", 1)
|
||||
U.show_message("<span class='notice'>Success!</span>", MSG_VISUAL)
|
||||
target.explode()
|
||||
else
|
||||
to_chat(U, "PDA not found.")
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
. += "<span class='notice'>Can be used again to interrupt the effect early. The recharge time is the same as the time spent in desync.</span>"
|
||||
|
||||
/obj/item/desynchronizer/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
return
|
||||
var/new_duration = input(user, "Set the duration (5-300):", "Desynchronizer", duration / 10) as null|num
|
||||
@@ -40,6 +41,7 @@
|
||||
new_duration = CLAMP(new_duration, 50, max_duration)
|
||||
duration = new_duration
|
||||
to_chat(user, "<span class='notice'>You set the duration to [DisplayTimeText(duration)].</span>")
|
||||
return TRUE
|
||||
|
||||
/obj/item/desynchronizer/proc/desync(mob/living/user)
|
||||
if(sync_holder)
|
||||
|
||||
@@ -153,6 +153,7 @@
|
||||
playsound(loc, voracious ? 'sound/effects/splat.ogg' : 'sound/effects/bin_close.ogg', 50, 1)
|
||||
items_preserved.Cut()
|
||||
cleaning = FALSE
|
||||
patient = null
|
||||
if(hound)
|
||||
update_gut(hound)
|
||||
|
||||
@@ -525,5 +526,5 @@
|
||||
playsound(hound, 'sound/effects/bin_close.ogg', 80, 1)
|
||||
|
||||
/obj/item/dogborg/sleeper/K9/flavour
|
||||
name = "Mobile Sleeper"
|
||||
name = "Recreational Sleeper"
|
||||
desc = "A mounted, underslung sleeper, intended for holding willing occupants for leisurely purposes."
|
||||
@@ -431,6 +431,7 @@
|
||||
icon_state = "glowstick"
|
||||
item_state = "glowstick"
|
||||
grind_results = list("phenol" = 15, "hydrogen" = 10, "oxygen" = 5) //Meth-in-a-stick
|
||||
rad_flags = RAD_NO_CONTAMINATE
|
||||
var/fuel = 0
|
||||
|
||||
/obj/item/flashlight/glowstick/Initialize()
|
||||
|
||||
@@ -182,14 +182,16 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
|
||||
return ..()
|
||||
return
|
||||
if(!scanning)
|
||||
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
|
||||
return 0
|
||||
return TRUE
|
||||
radiation_count = 0
|
||||
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/geiger_counter/emag_act(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -45,9 +45,11 @@ GLOBAL_LIST_EMPTY(GPS_list)
|
||||
add_overlay("working")
|
||||
|
||||
/obj/item/gps/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
toggletracking(user)
|
||||
return TRUE
|
||||
|
||||
/obj/item/gps/proc/toggletracking(mob/user)
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
|
||||
@@ -69,14 +69,9 @@
|
||||
if (!user.IsAdvancedToolUser())
|
||||
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
|
||||
return
|
||||
if(HAS_TRAIT(user, TRAIT_NOGUNS))
|
||||
if(HAS_TRAIT(user, TRAIT_CHUNKYFINGERS))
|
||||
to_chat(user, "<span class='warning'>Your fingers can't press the button!</span>")
|
||||
return
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.dna.check_mutation(HULK))
|
||||
to_chat(user, "<span class='warning'>Your fingers can't press the button!</span>")
|
||||
return
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
. += "<span class='notice'>Insert [src] into an active quantum pad to link it.</span>"
|
||||
|
||||
/obj/item/quantum_keycard/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You start pressing [src]'s unlink button...</span>")
|
||||
if(do_after(user, 40, target = src))
|
||||
to_chat(user, "<span class='notice'>The keycard beeps twice and disconnects the quantum link.</span>")
|
||||
qpad = null
|
||||
return TRUE
|
||||
|
||||
/obj/item/quantum_keycard/update_icon()
|
||||
if(qpad)
|
||||
|
||||
@@ -326,8 +326,10 @@ GLOBAL_LIST_INIT(channel_tokens, list(
|
||||
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
|
||||
|
||||
/obj/item/radio/headset/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(!istype(user) || !Adjacent(user) || user.incapacitated())
|
||||
return
|
||||
if (command)
|
||||
use_command = !use_command
|
||||
to_chat(user, "<span class='notice'>You toggle high-volume mode [use_command ? "on" : "off"].</span>")
|
||||
return TRUE
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode)
|
||||
/obj/item/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if (message_mode == MODE_INTERCOM)
|
||||
return // Avoid hearing the same thing twice
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
signal.levels = list(T.z)
|
||||
signal.broadcast()
|
||||
|
||||
/obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
|
||||
/obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range)
|
||||
return
|
||||
|
||||
@@ -131,232 +131,228 @@ SLIME SCANNER
|
||||
mob_status = "<span class='alert'><b>Deceased</b></span>"
|
||||
oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
|
||||
to_chat(user, "<span class='danger'>Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!</span>")
|
||||
if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE
|
||||
to_chat(user, "<span class='danger'>Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!</span>")
|
||||
|
||||
var/msg = "<span class='info'>*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n"
|
||||
var/msg = "<span class='info'>*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]"
|
||||
|
||||
// Damage descriptions
|
||||
if(brute_loss > 10)
|
||||
msg += "\t<span class='alert'>[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.</span>\n"
|
||||
msg += "\n\t<span class='alert'>[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.</span>"
|
||||
if(fire_loss > 10)
|
||||
msg += "\t<span class='alert'>[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.</span>\n"
|
||||
msg += "\n\t<span class='alert'>[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.</span>"
|
||||
if(oxy_loss > 10)
|
||||
msg += "\t<span class='info'><span class='alert'>[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.</span>\n"
|
||||
msg += "\n\t<span class='info'><span class='alert'>[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.</span>"
|
||||
if(tox_loss > 10)
|
||||
msg += "\t<span class='alert'>[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.</span>\n"
|
||||
msg += "\n\t<span class='alert'>[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.</span>"
|
||||
if(M.getStaminaLoss())
|
||||
msg += "\t<span class='alert'>Subject appears to be suffering from fatigue.</span>\n"
|
||||
msg += "\n\t<span class='alert'>Subject appears to be suffering from fatigue.</span>"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Fatigue Level: [M.getStaminaLoss()]%.</span>\n"
|
||||
msg += "\n\t<span class='info'>Fatigue Level: [M.getStaminaLoss()]%.</span>"
|
||||
if (M.getCloneLoss())
|
||||
msg += "\t<span class='alert'>Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.</span>\n"
|
||||
msg += "\n\t<span class='alert'>Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.</span>"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Cellular Damage Level: [M.getCloneLoss()].</span>\n"
|
||||
if (!M.getorgan(/obj/item/organ/brain))
|
||||
to_chat(user, "\t<span class='alert'>Subject lacks a brain.</span>") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.)
|
||||
if(ishuman(M) && advanced) // Should I make this not advanced?
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/obj/item/organ/liver/L = H.getorganslot("liver")
|
||||
if(L)
|
||||
if(L.swelling > 20)
|
||||
msg += "\t<span class='danger'>Subject is suffering from an enlarged liver.</span>\n" //i.e. shrink their liver or give them a transplant.
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's liver is missing.</span>\n"
|
||||
var/obj/item/organ/tongue/T = H.getorganslot("tongue")
|
||||
if(T)
|
||||
if(T.damage > 40)
|
||||
msg += "\t<span class='danger'>Subject is suffering from severe burn tissue on their tongue.</span>\n" //i.e. their tongue is shot
|
||||
if(T.name == "fluffy tongue")
|
||||
msg += "\t<span class='danger'>Subject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's tongue is missing.</span>\n"
|
||||
var/obj/item/organ/lungs/Lung = H.getorganslot("lungs")
|
||||
if(Lung)
|
||||
if(Lung.damage > 150)
|
||||
msg += "\t<span class='danger'>Subject is suffering from acute emphysema leading to trouble breathing.</span>\n" //i.e. Their lungs are shot
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's lungs have collapsed from trauma!</span>\n"
|
||||
var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
|
||||
if(P)
|
||||
if(P.length>20)
|
||||
msg += "\t<span class='info'>Subject has a sizeable gentleman's organ at [P.length] inches.</span>\n"
|
||||
var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts")
|
||||
if(Br)
|
||||
if(Br.cached_size>5)
|
||||
msg += "\t<span class='info'>Subject has a sizeable bosom with a [Br.size] cup.</span>\n"
|
||||
|
||||
if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain))
|
||||
msg += "\t<span class='alert'>Subject's brain function is non-existent.</span>\n"
|
||||
else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120)
|
||||
msg += "\t<span class='alert'>Severe brain damage detected. Subject likely to have mental traumas.</span>\n"
|
||||
else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45)
|
||||
msg += "\t<span class='alert'>Brain damage detected.</span>\n"
|
||||
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
if(LAZYLEN(C.get_traumas()))
|
||||
var/list/trauma_text = list()
|
||||
for(var/datum/brain_trauma/B in C.get_traumas())
|
||||
var/trauma_desc = ""
|
||||
switch(B.resilience)
|
||||
if(TRAUMA_RESILIENCE_SURGERY)
|
||||
trauma_desc += "severe "
|
||||
if(TRAUMA_RESILIENCE_LOBOTOMY)
|
||||
trauma_desc += "deep-rooted "
|
||||
if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
trauma_desc += "permanent "
|
||||
trauma_desc += B.scan_desc
|
||||
trauma_text += trauma_desc
|
||||
msg += "\t<span class='alert'>Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].</span>\n"
|
||||
if(C.roundstart_quirks.len)
|
||||
msg += "\t<span class='info'>Subject has the following physiological traits: [C.get_trait_string()].</span>\n"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Brain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.</span>\n"
|
||||
if(M.radiation)
|
||||
msg += "\t<span class='alert'>Subject is irradiated.</span>\n"
|
||||
msg += "\t<span class='info'>Radiation Level: [M.radiation] rad</span>\n"
|
||||
|
||||
if(advanced && M.hallucinating())
|
||||
msg += "\t<span class='info'>Subject is hallucinating.</span>\n"
|
||||
|
||||
//MKUltra
|
||||
if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall))
|
||||
msg += "\t<span class='info'>Subject has abnormal brain fuctions.</span>\n"
|
||||
|
||||
//Astrogen shenanigans
|
||||
if(advanced && M.reagents.has_reagent("astral"))
|
||||
if(M.mind)
|
||||
msg += "\t<span class='danger'>Warning: subject may be possesed.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='notice'>Subject appears to be astrally projecting.</span>\n"
|
||||
|
||||
//Eyes and ears
|
||||
if(advanced)
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
||||
msg += "\t<span class='info'><b>==EAR STATUS==</b></span>\n"
|
||||
if(istype(ears))
|
||||
var/healthy = TRUE
|
||||
if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION))
|
||||
healthy = FALSE
|
||||
msg += "\t<span class='alert'>Subject is genetically deaf.</span>\n"
|
||||
else if(HAS_TRAIT(C, TRAIT_DEAF))
|
||||
healthy = FALSE
|
||||
msg += "\t<span class='alert'>Subject is deaf.</span>\n"
|
||||
else
|
||||
if(ears.damage)
|
||||
to_chat(user, "\t<span class='alert'>Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.</span>")
|
||||
healthy = FALSE
|
||||
if(ears.deaf)
|
||||
to_chat(user, "\t<span class='alert'>Subject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.</span>")
|
||||
healthy = FALSE
|
||||
if(healthy)
|
||||
msg += "\t<span class='info'>Healthy.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='alert'>Subject does not have ears.</span>\n"
|
||||
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
|
||||
msg += "\t<span class='info'><b>==EYE STATUS==</b></span>\n"
|
||||
if(istype(eyes))
|
||||
var/healthy = TRUE
|
||||
if(HAS_TRAIT(C, TRAIT_BLIND))
|
||||
msg += "\t<span class='alert'>Subject is blind.</span>\n"
|
||||
healthy = FALSE
|
||||
if(HAS_TRAIT(C, TRAIT_NEARSIGHT))
|
||||
msg += "\t<span class='alert'>Subject is nearsighted.</span>\n"
|
||||
healthy = FALSE
|
||||
if(eyes.damage > 30)
|
||||
msg += "\t<span class='alert'>Subject has severe eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
else if(eyes.damage > 20)
|
||||
msg += "\t<span class='alert'>Subject has significant eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
else if(eyes.damage)
|
||||
msg += "\t<span class='alert'>Subject has minor eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
if(healthy)
|
||||
msg += "\t<span class='info'>Healthy.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='alert'>Subject does not have eyes.</span>\n"
|
||||
msg += "\n\t<span class='info'>Cellular Damage Level: [M.getCloneLoss()].</span>"
|
||||
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/ldamage = H.return_liver_damage()
|
||||
if(ldamage > 10)
|
||||
msg += "\t<span class='alert'>[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.</span>\n"
|
||||
to_chat(user, msg)
|
||||
msg = ""
|
||||
|
||||
// Body part damage report
|
||||
var/list/dmgreport = list()
|
||||
if(iscarbon(M) && mode == 1)
|
||||
var/mob/living/carbon/C = M
|
||||
var/list/damaged = C.get_damaged_bodyparts(1,1)
|
||||
if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0)
|
||||
msg += "<span class='info'>\tDamage: <span class='info'><font color='red'>Brute</font></span>-<font color='#FF8000'>Burn</font>-<font color='green'>Toxin</font>-<font color='blue'>Suffocation</font>\n\t\tSpecifics: <font color='red'>[brute_loss]</font>-<font color='#FF8000'>[fire_loss]</font>-<font color='green'>[tox_loss]</font>-<font color='blue'>[oxy_loss]</font></span>\n"
|
||||
for(var/obj/item/bodypart/org in damaged)
|
||||
msg += "\t\t<span class='info'>[capitalize(org.name)]: [(org.brute_dam > 0) ? "<font color='red'>[org.brute_dam]</font></span>" : "<font color='red'>0</font>"]-[(org.burn_dam > 0) ? "<font color='#FF8000'>[org.burn_dam]</font>" : "<font color='#FF8000'>0</font>"]\n"
|
||||
dmgreport += "<table style='margin-left:33px'><tr><font face='Verdana'>\
|
||||
<td style='width: 90px;'><font color='#0000CC'>Damage:</font></td>\
|
||||
<td style='width: 55px;'><font color='red'><b>Brute</b></font></td>\
|
||||
<td style='width: 45px;'><font color='orange'><b>Burn</b></font></td>\
|
||||
<td style='width: 45px;'><font color='green'><b>Toxin</b></font></td>\
|
||||
<td style='width: 90px;'><font color='purple'><b>Suffocation</b></font></td></tr>\
|
||||
<tr><td><font color='#0000CC'>Overall:</font></td>\
|
||||
<td><font color='red'>[brute_loss]</font></td>\
|
||||
<td><font color='orange'>[fire_loss]</font></td>\
|
||||
<td><font color='green'>[tox_loss]</font></td>\
|
||||
<td><font color='purple'>[oxy_loss]</font></td></tr>"
|
||||
|
||||
for(var/o in damaged)
|
||||
var/obj/item/bodypart/org = o //head, left arm, right arm, etc.
|
||||
dmgreport += "<tr><td><font color='#0000CC'>[capitalize(org.name)]:</font></td>\
|
||||
<td><font color='red'>[(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]</font></td>\
|
||||
<td><font color='orange'>[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]</font></td></tr>"
|
||||
dmgreport += "</table>"
|
||||
to_chat(user, dmgreport.Join())
|
||||
|
||||
|
||||
//Organ damages report
|
||||
if(ishuman(M))
|
||||
var/heart_ded = FALSE
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/minor_damage
|
||||
var/major_damage
|
||||
var/max_damage
|
||||
var/report_organs = FALSE
|
||||
for(var/organ in C.internal_organs)
|
||||
var/temp_message
|
||||
var/damage_message
|
||||
var/obj/item/organ/O = organ
|
||||
|
||||
//Piece together the lists to be reported
|
||||
for(var/O in H.internal_organs)
|
||||
var/obj/item/organ/organ = O
|
||||
if(organ.organ_flags & ORGAN_FAILING)
|
||||
report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency
|
||||
if(max_damage)
|
||||
max_damage += ", " //prelude the organ if we've already reported an organ
|
||||
max_damage += organ.name //this just slaps the organ name into the string of text
|
||||
else
|
||||
max_damage = "\t<span class='alert'>Non-Functional Organs: " //our initial statement
|
||||
max_damage += organ.name
|
||||
else if(organ.damage > organ.high_threshold)
|
||||
report_organs = TRUE
|
||||
if(major_damage)
|
||||
major_damage += ", "
|
||||
major_damage += organ.name
|
||||
else
|
||||
major_damage = "\t<span class='info'>Severely Damaged Organs: "
|
||||
major_damage += organ.name
|
||||
else if(organ.damage > organ.low_threshold)
|
||||
report_organs = TRUE
|
||||
if(minor_damage)
|
||||
minor_damage += ", "
|
||||
minor_damage += organ.name
|
||||
else
|
||||
minor_damage = "\t<span class='info'>Mildly Damaged Organs: "
|
||||
minor_damage += organ.name
|
||||
//EYES
|
||||
if(istype(O, /obj/item/organ/eyes))
|
||||
var/obj/item/organ/eyes/eyes = O
|
||||
if(advanced)
|
||||
if(HAS_TRAIT(C, TRAIT_BLIND))
|
||||
temp_message += " <span class='alert'>Subject is blind.</span>"
|
||||
if(HAS_TRAIT(C, TRAIT_NEARSIGHT))
|
||||
temp_message += " <span class='alert'>Subject is nearsighted.</span>"
|
||||
if(eyes.damage > 30)
|
||||
damage_message += " <span class='alert'>Subject has severe eye damage.</span>"
|
||||
else if(eyes.damage > 20)
|
||||
damage_message += " <span class='alert'>Subject has significant eye damage.</span>"
|
||||
else if(eyes.damage)
|
||||
damage_message += " <span class='alert'>Subject has minor eye damage.</span>"
|
||||
|
||||
|
||||
//EARS
|
||||
else if(istype(O, /obj/item/organ/ears))
|
||||
var/obj/item/organ/ears/ears = O
|
||||
if(advanced)
|
||||
if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION))
|
||||
temp_message += " <span class='alert'>Subject is genetically deaf.</span>"
|
||||
else if(HAS_TRAIT(C, TRAIT_DEAF))
|
||||
temp_message += " <span class='alert'>Subject is deaf.</span>"
|
||||
else
|
||||
if(ears.damage)
|
||||
damage_message += " <span class='alert'>Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.</span>"
|
||||
if(ears.deaf)
|
||||
damage_message += " <span class='alert'>Subject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.</span>"
|
||||
|
||||
|
||||
//BRAIN
|
||||
else if(istype(O, /obj/item/organ/brain))
|
||||
if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200)
|
||||
damage_message += " <span class='alert'>Subject's brain non-functional. Neurine injection recomended.</span>"
|
||||
else if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120)
|
||||
damage_message += " <span class='alert'>Severe brain damage detected. Subject likely to have mental traumas.</span>"
|
||||
else if (C.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45)
|
||||
damage_message += " <span class='alert'>Brain damage detected.</span>"
|
||||
if(advanced)
|
||||
temp_message += " <span class='info'>Brain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.</span>"
|
||||
|
||||
//TRAUMAS
|
||||
if(LAZYLEN(C.get_traumas()))
|
||||
var/list/trauma_text = list()
|
||||
for(var/datum/brain_trauma/B in C.get_traumas())
|
||||
var/trauma_desc = ""
|
||||
switch(B.resilience)
|
||||
if(TRAUMA_RESILIENCE_SURGERY)
|
||||
trauma_desc += "severe "
|
||||
if(TRAUMA_RESILIENCE_LOBOTOMY)
|
||||
trauma_desc += "deep-rooted "
|
||||
if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
trauma_desc += "permanent "
|
||||
trauma_desc += B.scan_desc
|
||||
trauma_text += trauma_desc
|
||||
temp_message += " <span class='alert'>Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].</span>"
|
||||
if(C.roundstart_quirks.len)
|
||||
temp_message += " <span class='info'>Subject has the following physiological traits: [C.get_trait_string()].</span>"
|
||||
|
||||
if(ishuman(C) && advanced)
|
||||
//MON PETIT CHAUFFEUR
|
||||
if(H.hallucinating())
|
||||
temp_message += " <span class='info'>Subject is hallucinating.</span>"
|
||||
|
||||
//MKUltra
|
||||
if(H.has_status_effect(/datum/status_effect/chem/enthrall))
|
||||
temp_message += " <span class='info'>Subject has abnormal brain fuctions.</span>"
|
||||
|
||||
//Astrogen shenanigans
|
||||
if(H.reagents.has_reagent("astral"))
|
||||
if(H.mind)
|
||||
temp_message += " <span class='danger'>Warning: subject may be possesed.</span>"
|
||||
else
|
||||
temp_message += " <span class='notice'>Subject appears to be astrally projecting.</span>"
|
||||
|
||||
|
||||
//LIVER
|
||||
else if(istype(O, /obj/item/organ/liver))
|
||||
var/obj/item/organ/liver/L = O
|
||||
if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated
|
||||
temp_message += "<span class='danger'>Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!</span>"
|
||||
if(L.swelling > 20)
|
||||
temp_message += " <span class='danger'>Subject is suffering from an enlarged liver.</span>" //i.e. shrink their liver or give them a transplant.
|
||||
|
||||
//HEART
|
||||
else if(ishuman(M) && (istype(O, /obj/item/organ/heart)))
|
||||
var/obj/item/organ/heart/He = O
|
||||
if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
|
||||
temp_message += " <span class='danger'>Subject suffering from heart attack: Apply defibrillation or other electric shock <b>immediately!</b></span>"
|
||||
if(He.organ_flags & ORGAN_FAILING)
|
||||
heart_ded = TRUE
|
||||
|
||||
//TONGUE
|
||||
else if(istype(O, /obj/item/organ/tongue))
|
||||
var/obj/item/organ/tongue/T = O
|
||||
if(T.name == "fluffy tongue")
|
||||
temp_message += " <span class='danger'>Subject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.</span>"
|
||||
|
||||
//HECK
|
||||
else if(istype(O, /obj/item/organ/genital/penis))
|
||||
var/obj/item/organ/genital/penis/P = O
|
||||
if(P.length>20)
|
||||
temp_message += " <span class='info'>Subject has a sizeable gentleman's organ at [P.length] inches.</span>"
|
||||
|
||||
else if(istype(O, /obj/item/organ/genital/breasts))
|
||||
var/obj/item/organ/genital/breasts/Br = O
|
||||
if(Br.cached_size>5)
|
||||
temp_message += " <span class='info'>Subject has a sizeable bosom with a [Br.size] cup.</span>"
|
||||
|
||||
|
||||
|
||||
//GENERAL HANDLER
|
||||
if(!damage_message)
|
||||
if(O.organ_flags & ORGAN_FAILING)
|
||||
damage_message += " <span class='alert'><b>End Stage [O.name] failure detected.</b></span>"
|
||||
else if(O.damage > O.high_threshold)
|
||||
damage_message += " <span class='alert'>Chronic [O.name] failure detected.</span>"
|
||||
else if(O.damage > O.low_threshold && advanced)
|
||||
damage_message += " <font color='red'>Acute [O.name] failure detected.</span>"
|
||||
|
||||
if(temp_message || damage_message)
|
||||
msg += "\t<b><span class='info'>[uppertext(O.name)]:</b></span> [damage_message] [temp_message]\n"
|
||||
|
||||
|
||||
|
||||
//END; LOOK FOR MISSING ORGANS?
|
||||
var/breathes = TRUE
|
||||
var/blooded = TRUE
|
||||
if(C.dna && C.dna.species)
|
||||
if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
|
||||
breathes = FALSE
|
||||
if(NOBLOOD in C.dna.species.species_traits)
|
||||
blooded = FALSE
|
||||
var/has_liver = (!(NOLIVER in C.dna.species.species_traits))
|
||||
var/has_stomach = (!(NOSTOMACH in C.dna.species.species_traits))
|
||||
if(!M.getorganslot(ORGAN_SLOT_EYES))
|
||||
msg += "\t<span class='alert'><b>Subject does not have eyes.</b></span>\n"
|
||||
if(!M.getorganslot(ORGAN_SLOT_EARS))
|
||||
msg += "\t<span class='alert'><b>Subject does not have ears.</b></span>\n"
|
||||
if(!M.getorganslot(ORGAN_SLOT_BRAIN))
|
||||
msg += "\t<span class='alert'><b>Subject's brain function is non-existent!</b></span>\n"
|
||||
if(has_liver && !M.getorganslot(ORGAN_SLOT_LIVER))
|
||||
msg += "\t<span class='alert'><b>Subject's liver is missing!</b></span>\n"
|
||||
if(blooded && !M.getorganslot(ORGAN_SLOT_HEART))
|
||||
msg += "\t<span class='alert'><b>Subject's heart is missing!</b></span>\n"
|
||||
if(breathes && !M.getorganslot(ORGAN_SLOT_LUNGS))
|
||||
msg += "\t<span class='alert'><b>Subject's lungs have collapsed from trauma!</b></span>\n"
|
||||
if(has_stomach && !M.getorganslot(ORGAN_SLOT_STOMACH))
|
||||
msg += "\t<span class='alert'><b>Subject's stomach is missing!</span>\n"
|
||||
|
||||
|
||||
if(M.radiation)
|
||||
msg += "\t<span class='alert'>Subject is irradiated.</span>\n"
|
||||
msg += "\t<span class='info'>Radiation Level: [M.radiation] rad</span>\n"
|
||||
|
||||
if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category
|
||||
if(!max_damage)
|
||||
max_damage = "\t<span class='alert'>Non-Functional Organs: </span>\n"
|
||||
else
|
||||
max_damage += "</span>\n"
|
||||
if(!major_damage)
|
||||
major_damage = "\t<span class='info'>Severely Damaged Organs: </span>\n"
|
||||
else
|
||||
major_damage += "</span>\n"
|
||||
if(!minor_damage)
|
||||
minor_damage = "\t<span class='info'>Mildly Damaged Organs: </span>\n"
|
||||
else
|
||||
minor_damage += "</span>\n"
|
||||
msg += "[minor_damage]"
|
||||
msg += "[major_damage]"
|
||||
msg += "[max_damage]"
|
||||
|
||||
|
||||
// Species and body temperature
|
||||
var/mob/living/carbon/human/H = M //Start to use human only stuff here
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/datum/species/S = H.dna.species
|
||||
var/mutant = FALSE
|
||||
if (H.dna.check_mutation(HULK))
|
||||
@@ -393,7 +389,15 @@ SLIME SCANNER
|
||||
msg += "<span class='info'>Time of Death:</span> [M.tod]\n"
|
||||
var/tdelta = round(world.time - M.timeofdeath)
|
||||
if(tdelta < (DEFIB_TIME_LIMIT * 10))
|
||||
msg += "<span class='danger'>Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!</span>\n"
|
||||
if(heart_ded)
|
||||
msg += "<span class='danger'>Subject died [DisplayTimeText(tdelta)] ago, heart requires surgical intervention for defibrillation.</span>"
|
||||
else
|
||||
msg += "<span class='danger'>Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!</span>"
|
||||
if(advanced)
|
||||
if(H.get_ghost() || H.key || H.client)//Since it can last a while.
|
||||
msg += "<span class='danger'> Intervention recommended.</span>\n"
|
||||
else
|
||||
msg += "\n"
|
||||
|
||||
for(var/thing in M.diseases)
|
||||
var/datum/disease/D = thing
|
||||
@@ -406,10 +410,9 @@ SLIME SCANNER
|
||||
var/blood_id = C.get_blood_id()
|
||||
if(blood_id)
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(H.bleed_rate)
|
||||
msg += "<span class='danger'>Subject is bleeding!</span>\n"
|
||||
var/blood_percent = round((C.blood_volume / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
|
||||
var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
|
||||
var/blood_type = C.dna.blood_type
|
||||
if(blood_id != ("blood" || "jellyblood"))//special blood substance
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
|
||||
@@ -417,12 +420,12 @@ SLIME SCANNER
|
||||
blood_type = R.name
|
||||
else
|
||||
blood_type = blood_id
|
||||
if(C.blood_volume <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.blood_volume > (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else if(C.blood_volume <= (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
if(C.scan_blood_volume() <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.scan_blood_volume() > (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else if(C.scan_blood_volume() <= (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else
|
||||
msg += "<span class='info'>Blood level [blood_percent] %, [C.blood_volume] cl, type: [blood_type]</span>\n"
|
||||
msg += "<span class='info'>Blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]</span>\n"
|
||||
|
||||
var/cyberimp_detect
|
||||
for(var/obj/item/organ/cyberimp/CI in C.internal_organs)
|
||||
@@ -431,7 +434,7 @@ SLIME SCANNER
|
||||
if(cyberimp_detect)
|
||||
msg += "<span class='notice'>Detected cybernetic modifications:</span>\n"
|
||||
msg += "<span class='notice'>[cyberimp_detect]</span>\n"
|
||||
msg += "*---------*</span>"
|
||||
msg += "<span class='notice'>*---------*</span>"
|
||||
to_chat(user, msg)
|
||||
SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE)
|
||||
|
||||
@@ -587,10 +590,10 @@ SLIME SCANNER
|
||||
to_chat(user, "<span class='info'>Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)</span>")
|
||||
|
||||
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
if(user.canUseTopic(src))
|
||||
|
||||
. = TRUE
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function is preparing itself.</span>")
|
||||
return
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
icon_state = "taperecorder_idle"
|
||||
|
||||
|
||||
/obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
|
||||
/obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
if(mytape && recording)
|
||||
mytape.timestamp += mytape.used_capacity
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
interact(user)
|
||||
return ..()
|
||||
|
||||
/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
|
||||
/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
|
||||
. = ..()
|
||||
last_message = raw_message
|
||||
|
||||
|
||||
@@ -138,11 +138,13 @@
|
||||
toggle_igniter(user)
|
||||
|
||||
/obj/item/flamethrower/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(ptank && isliving(user) && user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
user.put_in_hands(ptank)
|
||||
ptank = null
|
||||
to_chat(user, "<span class='notice'>You remove the plasma tank from [src]!</span>")
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/flamethrower/examine(mob/user)
|
||||
. = ..()
|
||||
|
||||
@@ -76,7 +76,7 @@ GLOBAL_LIST_EMPTY(possible_gifts)
|
||||
/obj/item/clothing/suit/poncho/red,
|
||||
/obj/item/clothing/suit/snowman,
|
||||
/obj/item/clothing/head/snowman,
|
||||
/obj/item/trash/coal)
|
||||
/obj/item/stack/sheet/mineral/coal)
|
||||
|
||||
gift_type_list += subtypesof(/obj/item/clothing/head/collectable)
|
||||
gift_type_list += subtypesof(/obj/item/toy) - (((typesof(/obj/item/toy/cards) - /obj/item/toy/cards/deck) + /obj/item/toy/figure + /obj/item/toy/ammo)) //All toys, except for abstract types and syndicate cards.
|
||||
|
||||
@@ -183,6 +183,23 @@
|
||||
explosion(user.loc, 1, 0, 2, 3, FALSE, FALSE, 2)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/book/granter/spell/nuclearfist
|
||||
spell = /obj/effect/proc_holder/spell/targeted/touch/nuclear_fist
|
||||
spellname = "nuclear fist"
|
||||
icon_state ="booknuclearfist"
|
||||
desc = "This book radiates confidence."
|
||||
remarks = list("Line them up....", ".. knock em' down...", "Dress in yellow for maximum effect... why?", "The energy comes from spinach... huh", "Work out for three years? No way!", "Oh I'll cast you a spell allright...", "What ho mighty wizard... ho ho ho...")
|
||||
|
||||
/obj/item/book/granter/spell/nuclearfist/recoil(mob/living/carbon/user)
|
||||
..()
|
||||
to_chat(user, "<span class='danger'>Your arm spontaneously detonates!</span>")
|
||||
explosion(user.loc, -1, 0, 2, -1, FALSE, FALSE, 2)
|
||||
var/obj/item/bodypart/part = user.get_holding_bodypart_of_item(src)
|
||||
if(part)
|
||||
part.dismember()
|
||||
qdel(part)
|
||||
|
||||
|
||||
/obj/item/book/granter/spell/sacredflame
|
||||
spell = /obj/effect/proc_holder/spell/targeted/sacred_flame
|
||||
spellname = "sacred flame"
|
||||
|
||||
@@ -14,23 +14,31 @@
|
||||
do_sparks(rand(5, 9), FALSE, src)
|
||||
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2)
|
||||
for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf))
|
||||
bang(get_turf(M), M)
|
||||
flashbang_mobs(flashbang_turf, flashbang_range)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/grenade/flashbang/proc/bang(turf/T , mob/living/M)
|
||||
/obj/item/grenade/flashbang/proc/flashbang_mobs(turf/source, range)
|
||||
var/list/banged = get_hearers_in_view(range, source)
|
||||
var/list/flashed = viewers(range, source)
|
||||
for(var/i in banged)
|
||||
bang(i, source)
|
||||
for(var/i in flashed)
|
||||
flash(i, source)
|
||||
|
||||
/obj/item/grenade/flashbang/proc/bang(mob/living/M, turf/source)
|
||||
if(M.stat == DEAD) //They're dead!
|
||||
return
|
||||
M.show_message("<span class='warning'>BANG</span>", 2)
|
||||
var/distance = max(0,get_dist(get_turf(src),T))
|
||||
|
||||
//Flash
|
||||
if(M.flash_act(affect_silicon = 1))
|
||||
M.Knockdown(max(200/max(1,distance), 60))
|
||||
//Bang
|
||||
M.show_message("<span class='warning'>BANG</span>", MSG_AUDIBLE)
|
||||
var/distance = get_dist(get_turf(M), source)
|
||||
if(!distance || loc == M || loc == M.loc) //Stop allahu akbarring rooms with this.
|
||||
M.Knockdown(200)
|
||||
M.soundbang_act(1, 200, 10, 15)
|
||||
|
||||
else
|
||||
M.soundbang_act(1, max(200/max(1,distance), 60), rand(0, 5))
|
||||
|
||||
/obj/item/grenade/flashbang/proc/flash(mob/living/M, turf/source)
|
||||
if(M.stat == DEAD) //They're dead!
|
||||
return
|
||||
var/distance = get_dist(get_turf(M), source)
|
||||
if(M.flash_act(affect_silicon = 1))
|
||||
M.Knockdown(max(200/max(1,distance), 60))
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/proc/get_data()
|
||||
return "No information available"
|
||||
return "No information available about this implant."
|
||||
|
||||
/obj/item/implant/dropped(mob/user)
|
||||
. = 1
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/brainwashed))
|
||||
target.mind.remove_antag_datum(/datum/antagonist/brainwashed)
|
||||
|
||||
if(target.mind.has_antag_datum(ANTAG_DATUM_VASSAL))
|
||||
SSticker.mode.remove_vassal(target.mind)
|
||||
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
|
||||
if(!silent)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
|
||||
|
||||
@@ -13,34 +13,31 @@
|
||||
var/broadcasting = null
|
||||
var/listening = 1
|
||||
|
||||
/obj/item/implantpad/examine(mob/user)
|
||||
. = ..()
|
||||
if(case)
|
||||
. += "<span class='notice'>Alt-click [src] to remove the inserted implant case.</span>"
|
||||
|
||||
/obj/item/implantpad/update_icon()
|
||||
if(case)
|
||||
icon_state = "implantpad-1"
|
||||
else
|
||||
icon_state = "implantpad-0"
|
||||
icon_state = "implantpad-[case ? TRUE : FALSE]"
|
||||
|
||||
|
||||
/obj/item/implantpad/attack_hand(mob/user)
|
||||
/obj/item/implantpad/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
if(case && user.is_holding(src))
|
||||
user.put_in_active_hand(case)
|
||||
if(case && user.can_hold_items() && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
user.put_in_hands(case)
|
||||
|
||||
case.add_fingerprint(user)
|
||||
case = null
|
||||
|
||||
add_fingerprint(user)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/item/implantpad/attackby(obj/item/implantcase/C, mob/user, params)
|
||||
if(istype(C, /obj/item/implantcase))
|
||||
if(!case)
|
||||
if(!user.transferItemToLoc(C, src))
|
||||
return
|
||||
if(istype(C))
|
||||
if(!case && user.transferItemToLoc(C, src))
|
||||
case = C
|
||||
update_icon()
|
||||
update_icon()
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user